update included libusb for mac to 1.0.24 (#744)
authortsteven4 <13596209+tsteven4@users.noreply.github.com>
Mon, 25 Oct 2021 11:26:34 +0000 (05:26 -0600)
committerGitHub <noreply@github.com>
Mon, 25 Oct 2021 11:26:34 +0000 (05:26 -0600)
24 files changed:
CMakeLists.txt
libusb.pri
mac/libusb/README
mac/libusb/Xcode/config.h
mac/libusb/core.c
mac/libusb/descriptor.c
mac/libusb/hotplug.c
mac/libusb/hotplug.h
mac/libusb/io.c
mac/libusb/libusb.h
mac/libusb/libusb.pro
mac/libusb/libusbi.h
mac/libusb/os/darwin_usb.c
mac/libusb/os/darwin_usb.h
mac/libusb/os/events_posix.c [new file with mode: 0644]
mac/libusb/os/events_posix.h [new file with mode: 0644]
mac/libusb/os/poll_posix.c [deleted file]
mac/libusb/os/poll_posix.h [deleted file]
mac/libusb/os/threads_posix.c
mac/libusb/os/threads_posix.h
mac/libusb/strerror.c
mac/libusb/sync.c
mac/libusb/version.h
mac/libusb/version_nano.h

index 1ccb7b6a8f56a87a47456f79d8518cea6b313143..65358f70279922a9690e6a5e40b4414be0aff25d 100644 (file)
@@ -241,7 +241,7 @@ if(APPLE)
     mac/libusb/strerror.c
     mac/libusb/sync.c
     mac/libusb/os/darwin_usb.c
-    mac/libusb/os/poll_posix.c
+    mac/libusb/os/events_posix.c
     mac/libusb/os/threads_posix.c
   )
   set(HEADERS ${HEADERS}
@@ -251,7 +251,7 @@ if(APPLE)
     mac/libusb/version.h
     mac/libusb/version_nano.h
     mac/libusb/os/darwin_usb.h
-    mac/libusb/os/poll_posix.h
+    mac/libusb/os/events_posix.h
     mac/libusb/os/threads_posix.h
   )
   add_compile_options(-Wall -Wsign-compare)
index 477929185164c8dae72768e45af27978a522688b..7569f113aaaa4b988721e6f4cfa2a87dd8db69d4 100644 (file)
@@ -42,7 +42,7 @@ macx|linux|openbsd {
                      mac/libusb/strerror.c \
                      mac/libusb/sync.c \
                      mac/libusb/os/darwin_usb.c \
-                     mac/libusb/os/poll_posix.c \
+                     mac/libusb/os/events_posix.c \
                      mac/libusb/os/threads_posix.c
           HEADERS += mac/libusb/hotplug.h \
                      mac/libusb/libusb.h \
@@ -50,7 +50,7 @@ macx|linux|openbsd {
                      mac/libusb/version.h \
                      mac/libusb/version_nano.h \
                      mac/libusb/os/darwin_usb.h \
-                     mac/libusb/os/poll_posix.h \
+                     mac/libusb/os/events_posix.h \
                      mac/libusb/os/threads_posix.h
         } else:equals(WITH_LIBUSB, custom) {
           message("libusb-1.0 is enabled but but must be manually configured")
index 498ebd813514b62dd1e679e83f958722e09242c2..77ef9b8a0822a942f853d1fe03245e1a48eb4fdf 100644 (file)
@@ -1,4 +1,4 @@
-This is libusb-1.0.22 from https://libusb.info/.
+This is libusb-1.0.24 from https://libusb.info/.
 Since we have such problems with people
 getting libusb successfully built - between the Universal Build issues
 and the fact that we have to work hard to go find where it's installed
index 14d152729de9ab2639bed5c90b677295a54d4336..59f3463352269488da3f41ff4064497dec29e6d1 100644 (file)
@@ -1,25 +1,37 @@
 /* config.h.  Manually generated for Xcode.  */
 
-/* Default visibility */
-#define DEFAULT_VISIBILITY /**/
+#include <AvailabilityMacros.h>
 
-/* Message logging */
+/* Define to the attribute for default visibility. */
+#define DEFAULT_VISIBILITY __attribute__ ((visibility ("default")))
+
+/* Define to 1 to enable message logging. */
 #define ENABLE_LOGGING 1
 
-/* Define to 1 if you have the <poll.h> header file. */
-#define HAVE_POLL_H 1
+/* On 10.12 and later, use newly available clock_*() functions */
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+/* Define to 1 if you have the `clock_gettime' function. */
+#define HAVE_CLOCK_GETTIME 1
+#endif
+
+/* On 10.6 and later, use newly available pthread_threadid_np() function */
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
+/* Define to 1 if you have the 'pthread_threadid_np' function. */
+#define HAVE_PTHREAD_THREADID_NP 1
+#endif
+
+/* Define to 1 if the system has the type `nfds_t'. */
+#define HAVE_NFDS_T 1
 
 /* Define to 1 if you have the <sys/time.h> header file. */
 #define HAVE_SYS_TIME_H 1
 
-/* Darwin backend */
-#define OS_DARWIN 1
-
-/* type of second poll() argument */
-#define POLL_NFDS_TYPE nfds_t
+/* Define to 1 if compiling for a POSIX platform. */
+#define PLATFORM_POSIX 1
 
-/* Use POSIX Threads */
-#define THREADS_POSIX 1
+/* Define to the attribute for enabling parameter checks on printf-like
+   functions. */
+#define PRINTF_FORMAT(a, b) __attribute__ ((__format__ (__printf__, a, b)))
 
-/* Use GNU extensions */
+/* Enable GNU extensions. */
 #define _GNU_SOURCE 1
index 50f92f6b1b46a5840255e29eca234bab1aaeca3c..07d459c1269c64b39ce2e83b59bec9a3fd9626ab 100644 (file)
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#include "config.h"
+#include "libusbi.h"
+#include "hotplug.h"
+#include "version.h"
 
-#include <errno.h>
-#include <stdarg.h>
+#ifdef __ANDROID__
+#include <android/log.h>
+#endif
 #include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-#ifdef HAVE_SYSLOG_H
+#ifdef HAVE_SYSLOG
 #include <syslog.h>
 #endif
 
-#ifdef __ANDROID__
-#include <android/log.h>
-#endif
-
-#include "libusbi.h"
-#include "hotplug.h"
-
-struct libusb_context *usbi_default_context = NULL;
+struct libusb_context *usbi_default_context;
 static const struct libusb_version libusb_version_internal =
        { LIBUSB_MAJOR, LIBUSB_MINOR, LIBUSB_MICRO, LIBUSB_NANO,
          LIBUSB_RC, "http://libusb.info" };
-static int default_context_refcnt = 0;
+static int default_context_refcnt;
 static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER;
-static struct timespec timestamp_origin = { 0, 0 };
+static struct timespec timestamp_origin;
+#if defined(ENABLE_LOGGING) && !defined(USE_SYSTEM_LOGGING_FACILITY)
+static libusb_log_cb log_handler;
+#endif
 
 usbi_mutex_static_t active_contexts_lock = USBI_MUTEX_INITIALIZER;
 struct list_head active_contexts_list;
@@ -61,7 +53,7 @@ struct list_head active_contexts_list;
  * \section intro Introduction
  *
  * libusb is an open source library that allows you to communicate with USB
- * devices from userspace. For more info, see the
+ * devices from user space. For more info, see the
  * <a href="http://libusb.info">libusb homepage</a>.
  *
  * This documentation is aimed at application developers wishing to
@@ -165,6 +157,36 @@ struct list_head active_contexts_list;
 /**
  * \page libusb_caveats Caveats
  *
+ * \section threadsafety Thread safety
+ *
+ * libusb is designed to be completely thread-safe, but as with any API it
+ * cannot prevent a user from sabotaging themselves, either intentionally or
+ * otherwise.
+ *
+ * Observe the following general guidelines:
+ *
+ * - Calls to functions that release a resource (e.g. libusb_close(),
+ *   libusb_free_config_descriptor()) should not be called concurrently on
+ *   the same resource. This is no different than concurrently calling free()
+ *   on the same allocated pointer.
+ * - Each individual \ref libusb_transfer should be prepared by a single
+ *   thread. In other words, no two threads should ever be concurrently
+ *   filling out the fields of a \ref libusb_transfer. You can liken this to
+ *   calling sprintf() with the same destination buffer from multiple threads.
+ *   The results will likely not be what you want unless the input parameters
+ *   are all the same, but its best to avoid this situation entirely.
+ * - Both the \ref libusb_transfer structure and its associated data buffer
+ *   should not be accessed between the time the transfer is submitted and the
+ *   time the completion callback is invoked. You can think of "ownership" of
+ *   these things as being transferred to libusb while the transfer is active.
+ * - The various "setter" functions (e.g. libusb_set_log_cb(),
+ *   libusb_set_pollfd_notifiers()) should not be called concurrently on the
+ *   resource. Though doing so will not lead to any undefined behavior, it
+ *   will likely produce results that the application does not expect.
+ *
+ * Rules for multiple threads and asynchronous I/O are detailed
+ * \ref libusb_mtasync "here".
+ *
  * \section fork Fork considerations
  *
  * libusb is <em>not</em> designed to work across fork() calls. Depending on
@@ -191,12 +213,12 @@ struct list_head active_contexts_list;
  * you when this has happened, so if someone else resets your device it will
  * not be clear to your own program why the device state has changed.
  *
- * Ultimately, this is a limitation of writing drivers in userspace.
+ * Ultimately, this is a limitation of writing drivers in user space.
  * Separation from the USB stack in the underlying kernel makes it difficult
  * for the operating system to deliver such notifications to your program.
  * The Linux kernel USB stack allows such reset notifications to be delivered
  * to in-kernel USB drivers, but it is not clear how such notifications could
- * be delivered to second-class drivers that live in userspace.
+ * be delivered to second-class drivers that live in user space.
  *
  * \section blockonly Blocking-only functionality
  *
@@ -424,6 +446,7 @@ if (cfg != desired)
   * - libusb_set_auto_detach_kernel_driver()
   * - libusb_set_configuration()
   * - libusb_set_debug()
+  * - libusb_set_log_cb()
   * - libusb_set_interface_alt_setting()
   * - libusb_set_iso_packet_lengths()
   * - libusb_set_option()
@@ -438,6 +461,7 @@ if (cfg != desired)
   * - libusb_unlock_event_waiters()
   * - libusb_unref_device()
   * - libusb_wait_for_event()
+  * - libusb_wrap_sys_device()
   *
   * \section Structures
   * - libusb_bos_descriptor
@@ -466,6 +490,7 @@ if (cfg != desired)
   * - \ref libusb_class_code
   * - \ref libusb_descriptor_type
   * - \ref libusb_endpoint_direction
+  * - \ref libusb_endpoint_transfer_type
   * - \ref libusb_error
   * - \ref libusb_iso_sync_type
   * - \ref libusb_iso_usage_type
@@ -674,17 +699,12 @@ struct libusb_device *usbi_alloc_device(struct libusb_context *ctx,
        unsigned long session_id)
 {
        size_t priv_size = usbi_backend.device_priv_size;
-       struct libusb_device *dev = calloc(1, sizeof(*dev) + priv_size);
-       int r;
+       struct libusb_device *dev = calloc(1, PTR_ALIGN(sizeof(*dev)) + priv_size);
 
        if (!dev)
                return NULL;
 
-       r = usbi_mutex_init(&dev->lock);
-       if (r) {
-               free(dev);
-               return NULL;
-       }
+       usbi_mutex_init(&dev->lock);
 
        dev->ctx = ctx;
        dev->refcnt = 1;
@@ -742,21 +762,22 @@ void usbi_disconnect_device(struct libusb_device *dev)
  * to the discovered device list. */
 int usbi_sanitize_device(struct libusb_device *dev)
 {
-       int r;
        uint8_t num_configurations;
 
-       r = usbi_device_cache_descriptor(dev);
-       if (r < 0)
-               return r;
+       if (dev->device_descriptor.bLength != LIBUSB_DT_DEVICE_SIZE ||
+           dev->device_descriptor.bDescriptorType != LIBUSB_DT_DEVICE) {
+               usbi_err(DEVICE_CTX(dev), "invalid device descriptor");
+               return LIBUSB_ERROR_IO;
+       }
 
        num_configurations = dev->device_descriptor.bNumConfigurations;
        if (num_configurations > USB_MAXCONFIG) {
                usbi_err(DEVICE_CTX(dev), "too many configurations");
                return LIBUSB_ERROR_IO;
-       } else if (0 == num_configurations)
+       } else if (0 == num_configurations) {
                usbi_dbg("zero configurations, maybe an unauthorized device");
+       }
 
-       dev->num_configurations = num_configurations;
        return 0;
 }
 
@@ -770,11 +791,12 @@ struct libusb_device *usbi_get_device_by_session_id(struct libusb_context *ctx,
        struct libusb_device *ret = NULL;
 
        usbi_mutex_lock(&ctx->usb_devs_lock);
-       list_for_each_entry(dev, &ctx->usb_devs, list, struct libusb_device)
+       for_each_device(ctx, dev) {
                if (dev->session_data == session_id) {
                        ret = libusb_ref_device(dev);
                        break;
                }
+       }
        usbi_mutex_unlock(&ctx->usb_devs_lock);
 
        return ret;
@@ -807,12 +829,14 @@ ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx,
        struct libusb_device **ret;
        int r = 0;
        ssize_t i, len;
-       USBI_GET_CONTEXT(ctx);
-       usbi_dbg("");
+
+       usbi_dbg(" ");
 
        if (!discdevs)
                return LIBUSB_ERROR_NO_MEM;
 
+       ctx = usbi_get_context(ctx);
+
        if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
                /* backend provides hotplug support */
                struct libusb_device *dev;
@@ -821,7 +845,7 @@ ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx,
                        usbi_backend.hotplug_poll();
 
                usbi_mutex_lock(&ctx->usb_devs_lock);
-               list_for_each_entry(dev, &ctx->usb_devs, list, struct libusb_device) {
+               for_each_device(ctx, dev) {
                        discdevs = discovered_devs_append(discdevs, dev);
 
                        if (!discdevs) {
@@ -841,8 +865,8 @@ ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx,
        }
 
        /* convert discovered_devs into a list */
-       len = discdevs->len;
-       ret = calloc(len + 1, sizeof(struct libusb_device *));
+       len = (ssize_t)discdevs->len;
+       ret = calloc((size_t)len + 1, sizeof(struct libusb_device *));
        if (!ret) {
                len = LIBUSB_ERROR_NO_MEM;
                goto out;
@@ -925,7 +949,7 @@ uint8_t API_EXPORTED libusb_get_port_number(libusb_device *dev)
  * \returns LIBUSB_ERROR_OVERFLOW if the array is too small
  */
 int API_EXPORTED libusb_get_port_numbers(libusb_device *dev,
-       uint8_tport_numbers, int port_numbers_len)
+       uint8_t *port_numbers, int port_numbers_len)
 {
        int i = port_numbers_len;
        struct libusb_context *ctx = DEVICE_CTX(dev);
@@ -948,10 +972,10 @@ int API_EXPORTED libusb_get_port_numbers(libusb_device *dev,
 }
 
 /** \ingroup libusb_dev
- * Deprecated please use libusb_get_port_numbers instead.
+ * \deprecated Please use \ref libusb_get_port_numbers() instead.
  */
 int API_EXPORTED libusb_get_port_path(libusb_context *ctx, libusb_device *dev,
-       uint8_tport_numbers, uint8_t port_numbers_len)
+       uint8_t *port_numbers, uint8_t port_numbers_len)
 {
        UNUSED(ctx);
 
@@ -966,7 +990,7 @@ int API_EXPORTED libusb_get_port_path(libusb_context *ctx, libusb_device *dev,
  * function and make sure that you only access the parent before issuing
  * \ref libusb_free_device_list(). The reason is that libusb currently does
  * not maintain a permanent list of device instances, and therefore can
- * only guarantee that parents are fully instantiated within a 
+ * only guarantee that parents are fully instantiated within a
  * libusb_get_device_list() - libusb_free_device_list() block.
  */
 DEFAULT_VISIBILITY
@@ -1075,7 +1099,9 @@ out:
  * If acting on an isochronous or interrupt endpoint, this function will
  * multiply the value found in bits 0:10 by the number of transactions per
  * microframe (determined by bits 11:12). Otherwise, this function just
- * returns the numeric value found in bits 0:10.
+ * returns the numeric value found in bits 0:10. For USB 3.0 device, it
+ * will attempts to retrieve the Endpoint Companion Descriptor to return
+ * wBytesPerInterval.
  *
  * This function is useful for setting up isochronous transfers, for example
  * you might pass the return value from this function to
@@ -1095,9 +1121,11 @@ int API_EXPORTED libusb_get_max_iso_packet_size(libusb_device *dev,
 {
        struct libusb_config_descriptor *config;
        const struct libusb_endpoint_descriptor *ep;
-       enum libusb_transfer_type ep_type;
+       struct libusb_ss_endpoint_companion_descriptor *ss_ep_cmp;
+       enum libusb_endpoint_transfer_type ep_type;
        uint16_t val;
        int r;
+       int speed;
 
        r = libusb_get_active_config_descriptor(dev, &config);
        if (r < 0) {
@@ -1112,13 +1140,25 @@ int API_EXPORTED libusb_get_max_iso_packet_size(libusb_device *dev,
                goto out;
        }
 
-       val = ep->wMaxPacketSize;
-       ep_type = (enum libusb_transfer_type) (ep->bmAttributes & 0x3);
+       speed = libusb_get_device_speed(dev);
+       if (speed >= LIBUSB_SPEED_SUPER) {
+               r = libusb_get_ss_endpoint_companion_descriptor(dev->ctx, ep, &ss_ep_cmp);
+               if (r == LIBUSB_SUCCESS) {
+                       r = ss_ep_cmp->wBytesPerInterval;
+                       libusb_free_ss_endpoint_companion_descriptor(ss_ep_cmp);
+               }
+       }
 
-       r = val & 0x07ff;
-       if (ep_type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS
-                       || ep_type == LIBUSB_TRANSFER_TYPE_INTERRUPT)
-               r *= (1 + ((val >> 11) & 3));
+       /* If the device isn't a SuperSpeed device or retrieving the SS endpoint didn't worked. */
+       if (speed < LIBUSB_SPEED_SUPER || r < 0) {
+               val = ep->wMaxPacketSize;
+               ep_type = (enum libusb_endpoint_transfer_type) (ep->bmAttributes & 0x3);
+
+               r = val & 0x07ff;
+               if (ep_type == LIBUSB_ENDPOINT_TRANSFER_TYPE_ISOCHRONOUS
+                   || ep_type == LIBUSB_ENDPOINT_TRANSFER_TYPE_INTERRUPT)
+                       r *= (1 + ((val >> 11) & 3));
+       }
 
 out:
        libusb_free_config_descriptor(config);
@@ -1173,41 +1213,70 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev)
        }
 }
 
-/*
- * Signal the event pipe so that the event handling thread will be
- * interrupted to process an internal event.
+/** \ingroup libusb_dev
+ * Wrap a platform-specific system device handle and obtain a libusb device
+ * handle for the underlying device. The handle allows you to use libusb to
+ * perform I/O on the device in question.
+ *
+ * Must call libusb_set_option(NULL, LIBUSB_OPTION_WEAK_AUTHORITY)
+ * before libusb_init if don't have authority to access the usb device directly.
+ *
+ * On Linux, the system device handle must be a valid file descriptor opened
+ * on the device node.
+ *
+ * The system device handle must remain open until libusb_close() is called.
+ * The system device handle will not be closed by libusb_close().
+ *
+ * Internally, this function creates a temporary device and makes it
+ * available to you through libusb_get_device(). This device is destroyed
+ * during libusb_close(). The device shall not be opened through libusb_open().
+ *
+ * This is a non-blocking function; no requests are sent over the bus.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param sys_dev the platform-specific system device handle
+ * \param dev_handle output location for the returned device handle pointer. Only
+ * populated when the return code is 0.
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NO_MEM on memory allocation failure
+ * \returns LIBUSB_ERROR_ACCESS if the user has insufficient permissions
+ * \returns LIBUSB_ERROR_NOT_SUPPORTED if the operation is not supported on this
+ * platform
+ * \returns another LIBUSB_ERROR code on other failure
  */
-int usbi_signal_event(struct libusb_context *ctx)
+int API_EXPORTED libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev,
+       libusb_device_handle **dev_handle)
 {
-       unsigned char dummy = 1;
-       ssize_t r;
+       struct libusb_device_handle *_dev_handle;
+       size_t priv_size = usbi_backend.device_handle_priv_size;
+       int r;
 
-       /* write some data on event pipe to interrupt event handlers */
-       r = usbi_write(ctx->event_pipe[1], &dummy, sizeof(dummy));
-       if (r != sizeof(dummy)) {
-               usbi_warn(ctx, "internal signalling write failed");
-               return LIBUSB_ERROR_IO;
-       }
+       usbi_dbg("wrap_sys_device 0x%" PRIxPTR, (uintptr_t)sys_dev);
 
-       return 0;
-}
+       ctx = usbi_get_context(ctx);
 
-/*
- * Clear the event pipe so that the event handling will no longer be
- * interrupted.
- */
-int usbi_clear_event(struct libusb_context *ctx)
-{
-       unsigned char dummy;
-       ssize_t r;
+       if (!usbi_backend.wrap_sys_device)
+               return LIBUSB_ERROR_NOT_SUPPORTED;
 
-       /* read some data on event pipe to clear it */
-       r = usbi_read(ctx->event_pipe[0], &dummy, sizeof(dummy));
-       if (r != sizeof(dummy)) {
-               usbi_warn(ctx, "internal signalling read failed");
-               return LIBUSB_ERROR_IO;
+       _dev_handle = calloc(1, PTR_ALIGN(sizeof(*_dev_handle)) + priv_size);
+       if (!_dev_handle)
+               return LIBUSB_ERROR_NO_MEM;
+
+       usbi_mutex_init(&_dev_handle->lock);
+
+       r = usbi_backend.wrap_sys_device(ctx, _dev_handle, sys_dev);
+       if (r < 0) {
+               usbi_dbg("wrap_sys_device 0x%" PRIxPTR " returns %d", (uintptr_t)sys_dev, r);
+               usbi_mutex_destroy(&_dev_handle->lock);
+               free(_dev_handle);
+               return r;
        }
 
+       usbi_mutex_lock(&ctx->open_devs_lock);
+       list_add(&_dev_handle->list, &ctx->open_devs);
+       usbi_mutex_unlock(&ctx->open_devs_lock);
+       *dev_handle = _dev_handle;
+
        return 0;
 }
 
@@ -1243,20 +1312,13 @@ int API_EXPORTED libusb_open(libusb_device *dev,
                return LIBUSB_ERROR_NO_DEVICE;
        }
 
-       _dev_handle = malloc(sizeof(*_dev_handle) + priv_size);
+       _dev_handle = calloc(1, PTR_ALIGN(sizeof(*_dev_handle)) + priv_size);
        if (!_dev_handle)
                return LIBUSB_ERROR_NO_MEM;
 
-       r = usbi_mutex_init(&_dev_handle->lock);
-       if (r) {
-               free(_dev_handle);
-               return LIBUSB_ERROR_OTHER;
-       }
+       usbi_mutex_init(&_dev_handle->lock);
 
        _dev_handle->dev = libusb_ref_device(dev);
-       _dev_handle->auto_detach_kernel_driver = 0;
-       _dev_handle->claimed_interfaces = 0;
-       memset(&_dev_handle->os_priv, 0, priv_size);
 
        r = usbi_backend.open(_dev_handle);
        if (r < 0) {
@@ -1337,7 +1399,7 @@ static void do_close(struct libusb_context *ctx,
        usbi_mutex_lock(&ctx->flying_transfers_lock);
 
        /* safe iteration because transfers may be being deleted */
-       list_for_each_entry_safe(itransfer, tmp, &ctx->flying_transfers, list, struct usbi_transfer) {
+       for_each_transfer_safe(ctx, itransfer, tmp) {
                struct libusb_transfer *transfer =
                        USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
 
@@ -1395,12 +1457,12 @@ static void do_close(struct libusb_context *ctx,
 void API_EXPORTED libusb_close(libusb_device_handle *dev_handle)
 {
        struct libusb_context *ctx;
+       unsigned int event_flags;
        int handling_events;
-       int pending_events;
 
        if (!dev_handle)
                return;
-       usbi_dbg("");
+       usbi_dbg(" ");
 
        ctx = HANDLE_CTX(dev_handle);
        handling_events = usbi_handling_events(ctx);
@@ -1417,10 +1479,11 @@ void API_EXPORTED libusb_close(libusb_device_handle *dev_handle)
                /* Record that we are closing a device.
                 * Only signal an event if there are no prior pending events. */
                usbi_mutex_lock(&ctx->event_data_lock);
-               pending_events = usbi_pending_events(ctx);
-               ctx->device_close++;
-               if (!pending_events)
-                       usbi_signal_event(ctx);
+               event_flags = ctx->event_flags;
+               if (!ctx->device_close++)
+                       ctx->event_flags |= USBI_EVENT_DEVICE_CLOSE;
+               if (!event_flags)
+                       usbi_signal_event(&ctx->event);
                usbi_mutex_unlock(&ctx->event_data_lock);
 
                /* take event handling lock */
@@ -1434,10 +1497,10 @@ void API_EXPORTED libusb_close(libusb_device_handle *dev_handle)
                /* We're done with closing this device.
                 * Clear the event pipe if there are no further pending events. */
                usbi_mutex_lock(&ctx->event_data_lock);
-               ctx->device_close--;
-               pending_events = usbi_pending_events(ctx);
-               if (!pending_events)
-                       usbi_clear_event(ctx);
+               if (!--ctx->device_close)
+                       ctx->event_flags &= ~USBI_EVENT_DEVICE_CLOSE;
+               if (!ctx->event_flags)
+                       usbi_clear_event(&ctx->event);
                usbi_mutex_unlock(&ctx->event_data_lock);
 
                /* Release event handling lock and wake up event waiters */
@@ -1482,29 +1545,30 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle,
        int *config)
 {
        int r = LIBUSB_ERROR_NOT_SUPPORTED;
+       uint8_t tmp = 0;
 
-       usbi_dbg("");
+       usbi_dbg(" ");
        if (usbi_backend.get_configuration)
-               r = usbi_backend.get_configuration(dev_handle, config);
+               r = usbi_backend.get_configuration(dev_handle, &tmp);
 
        if (r == LIBUSB_ERROR_NOT_SUPPORTED) {
-               uint8_t tmp = 0;
                usbi_dbg("falling back to control message");
                r = libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_IN,
                        LIBUSB_REQUEST_GET_CONFIGURATION, 0, 0, &tmp, 1, 1000);
-               if (r == 0) {
+               if (r == 1) {
+                       r = 0;
+               } else if (r == 0) {
                        usbi_err(HANDLE_CTX(dev_handle), "zero bytes returned in ctrl transfer?");
                        r = LIBUSB_ERROR_IO;
-               } else if (r == 1) {
-                       r = 0;
-                       *config = tmp;
                } else {
                        usbi_dbg("control failed, error %d", r);
                }
        }
 
-       if (r == 0)
-               usbi_dbg("active config %d", *config);
+       if (r == 0) {
+               usbi_dbg("active config %u", tmp);
+               *config = (int)tmp;
+       }
 
        return r;
 }
@@ -1523,6 +1587,11 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle,
  * causing most USB-related device state to be reset (altsetting reset to zero,
  * endpoint halts cleared, toggles reset).
  *
+ * Not all backends support setting the configuration from user space, which
+ * will be indicated by the return code LIBUSB_ERROR_NOT_SUPPORTED. As this
+ * suggests that the platform is handling the device configuration itself,
+ * this error should generally be safe to ignore.
+ *
  * You cannot change/reset configuration if your application has claimed
  * interfaces. It is advised to set the desired configuration before claiming
  * interfaces.
@@ -1552,6 +1621,8 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle,
  * \returns 0 on success
  * \returns LIBUSB_ERROR_NOT_FOUND if the requested configuration does not exist
  * \returns LIBUSB_ERROR_BUSY if interfaces are currently claimed
+ * \returns LIBUSB_ERROR_NOT_SUPPORTED if setting or changing the configuration
+ * is not supported by the backend
  * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
  * \returns another LIBUSB_ERROR code on other failure
  * \see libusb_set_auto_detach_kernel_driver()
@@ -1560,6 +1631,8 @@ int API_EXPORTED libusb_set_configuration(libusb_device_handle *dev_handle,
        int configuration)
 {
        usbi_dbg("configuration %d", configuration);
+       if (configuration < -1 || configuration > (int)UINT8_MAX)
+               return LIBUSB_ERROR_INVALID_PARAM;
        return usbi_backend.set_configuration(dev_handle, configuration);
 }
 
@@ -1597,19 +1670,19 @@ int API_EXPORTED libusb_claim_interface(libusb_device_handle *dev_handle,
        int r = 0;
 
        usbi_dbg("interface %d", interface_number);
-       if (interface_number >= USB_MAXINTERFACES)
+       if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
                return LIBUSB_ERROR_INVALID_PARAM;
 
        if (!dev_handle->dev->attached)
                return LIBUSB_ERROR_NO_DEVICE;
 
        usbi_mutex_lock(&dev_handle->lock);
-       if (dev_handle->claimed_interfaces & (1 << interface_number))
+       if (dev_handle->claimed_interfaces & (1U << interface_number))
                goto out;
 
-       r = usbi_backend.claim_interface(dev_handle, interface_number);
+       r = usbi_backend.claim_interface(dev_handle, (uint8_t)interface_number);
        if (r == 0)
-               dev_handle->claimed_interfaces |= 1 << interface_number;
+               dev_handle->claimed_interfaces |= 1U << interface_number;
 
 out:
        usbi_mutex_unlock(&dev_handle->lock);
@@ -1641,18 +1714,18 @@ int API_EXPORTED libusb_release_interface(libusb_device_handle *dev_handle,
        int r;
 
        usbi_dbg("interface %d", interface_number);
-       if (interface_number >= USB_MAXINTERFACES)
+       if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
                return LIBUSB_ERROR_INVALID_PARAM;
 
        usbi_mutex_lock(&dev_handle->lock);
-       if (!(dev_handle->claimed_interfaces & (1 << interface_number))) {
+       if (!(dev_handle->claimed_interfaces & (1U << interface_number))) {
                r = LIBUSB_ERROR_NOT_FOUND;
                goto out;
        }
 
-       r = usbi_backend.release_interface(dev_handle, interface_number);
+       r = usbi_backend.release_interface(dev_handle, (uint8_t)interface_number);
        if (r == 0)
-               dev_handle->claimed_interfaces &= ~(1 << interface_number);
+               dev_handle->claimed_interfaces &= ~(1U << interface_number);
 
 out:
        usbi_mutex_unlock(&dev_handle->lock);
@@ -1685,7 +1758,9 @@ int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_hand
 {
        usbi_dbg("interface %d altsetting %d",
                interface_number, alternate_setting);
-       if (interface_number >= USB_MAXINTERFACES)
+       if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
+               return LIBUSB_ERROR_INVALID_PARAM;
+       if (alternate_setting < 0 || alternate_setting > (int)UINT8_MAX)
                return LIBUSB_ERROR_INVALID_PARAM;
 
        usbi_mutex_lock(&dev_handle->lock);
@@ -1694,14 +1769,14 @@ int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_hand
                return LIBUSB_ERROR_NO_DEVICE;
        }
 
-       if (!(dev_handle->claimed_interfaces & (1 << interface_number))) {
+       if (!(dev_handle->claimed_interfaces & (1U << interface_number))) {
                usbi_mutex_unlock(&dev_handle->lock);
                return LIBUSB_ERROR_NOT_FOUND;
        }
        usbi_mutex_unlock(&dev_handle->lock);
 
-       return usbi_backend.set_interface_altsetting(dev_handle, interface_number,
-               alternate_setting);
+       return usbi_backend.set_interface_altsetting(dev_handle,
+               (uint8_t)interface_number, (uint8_t)alternate_setting);
 }
 
 /** \ingroup libusb_dev
@@ -1751,11 +1826,14 @@ int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev_handle,
  */
 int API_EXPORTED libusb_reset_device(libusb_device_handle *dev_handle)
 {
-       usbi_dbg("");
+       usbi_dbg(" ");
        if (!dev_handle->dev->attached)
                return LIBUSB_ERROR_NO_DEVICE;
 
-       return usbi_backend.reset_device(dev_handle);
+       if (usbi_backend.reset_device)
+               return usbi_backend.reset_device(dev_handle);
+       else
+               return LIBUSB_ERROR_NOT_SUPPORTED;
 }
 
 /** \ingroup libusb_asyncio
@@ -1782,7 +1860,10 @@ int API_EXPORTED libusb_reset_device(libusb_device_handle *dev_handle)
 int API_EXPORTED libusb_alloc_streams(libusb_device_handle *dev_handle,
        uint32_t num_streams, unsigned char *endpoints, int num_endpoints)
 {
-       usbi_dbg("streams %u eps %d", (unsigned) num_streams, num_endpoints);
+       usbi_dbg("streams %u eps %d", (unsigned)num_streams, num_endpoints);
+
+       if (!num_streams || !endpoints || num_endpoints <= 0)
+               return LIBUSB_ERROR_INVALID_PARAM;
 
        if (!dev_handle->dev->attached)
                return LIBUSB_ERROR_NO_DEVICE;
@@ -1811,6 +1892,9 @@ int API_EXPORTED libusb_free_streams(libusb_device_handle *dev_handle,
 {
        usbi_dbg("eps %d", num_endpoints);
 
+       if (!endpoints || num_endpoints <= 0)
+               return LIBUSB_ERROR_INVALID_PARAM;
+
        if (!dev_handle->dev->attached)
                return LIBUSB_ERROR_NO_DEVICE;
 
@@ -1833,7 +1917,7 @@ int API_EXPORTED libusb_free_streams(libusb_device_handle *dev_handle,
  * the same cache lines) when a transfer is in progress, although it is legal
  * to have several transfers going on within the same memory block.
  *
- * Will return NULL on failure. Many systems do not support such zerocopy
+ * Will return NULL on failure. Many systems do not support such zero-copy
  * and will always return NULL. Memory allocated with this function must be
  * freed with \ref libusb_dev_mem_free. Specifically, this means that the
  * flag \ref LIBUSB_TRANSFER_FREE_BUFFER cannot be used to free memory allocated
@@ -1897,11 +1981,14 @@ int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev_handle,
 {
        usbi_dbg("interface %d", interface_number);
 
+       if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
+               return LIBUSB_ERROR_INVALID_PARAM;
+
        if (!dev_handle->dev->attached)
                return LIBUSB_ERROR_NO_DEVICE;
 
        if (usbi_backend.kernel_driver_active)
-               return usbi_backend.kernel_driver_active(dev_handle, interface_number);
+               return usbi_backend.kernel_driver_active(dev_handle, (uint8_t)interface_number);
        else
                return LIBUSB_ERROR_NOT_SUPPORTED;
 }
@@ -1932,11 +2019,14 @@ int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev_handle,
 {
        usbi_dbg("interface %d", interface_number);
 
+       if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
+               return LIBUSB_ERROR_INVALID_PARAM;
+
        if (!dev_handle->dev->attached)
                return LIBUSB_ERROR_NO_DEVICE;
 
        if (usbi_backend.detach_kernel_driver)
-               return usbi_backend.detach_kernel_driver(dev_handle, interface_number);
+               return usbi_backend.detach_kernel_driver(dev_handle, (uint8_t)interface_number);
        else
                return LIBUSB_ERROR_NOT_SUPPORTED;
 }
@@ -1966,11 +2056,14 @@ int API_EXPORTED libusb_attach_kernel_driver(libusb_device_handle *dev_handle,
 {
        usbi_dbg("interface %d", interface_number);
 
+       if (interface_number < 0 || interface_number >= USB_MAXINTERFACES)
+               return LIBUSB_ERROR_INVALID_PARAM;
+
        if (!dev_handle->dev->attached)
                return LIBUSB_ERROR_NO_DEVICE;
 
        if (usbi_backend.attach_kernel_driver)
-               return usbi_backend.attach_kernel_driver(dev_handle, interface_number);
+               return usbi_backend.attach_kernel_driver(dev_handle, (uint8_t)interface_number);
        else
                return LIBUSB_ERROR_NOT_SUPPORTED;
 }
@@ -2014,7 +2107,7 @@ int API_EXPORTED libusb_set_auto_detach_kernel_driver(
 void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level)
 {
 #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
-       USBI_GET_CONTEXT(ctx);
+       ctx = usbi_get_context(ctx);
        if (!ctx->debug_fixed) {
                level = CLAMP(level, LIBUSB_LOG_LEVEL_NONE, LIBUSB_LOG_LEVEL_DEBUG);
                ctx->debug = (enum libusb_log_level)level;
@@ -2025,6 +2118,50 @@ void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level)
 #endif
 }
 
+/** \ingroup libusb_lib
+ * Set log handler.
+ *
+ * libusb will redirect its log messages to the provided callback function.
+ * libusb supports redirection of per context and global log messages.
+ * Log messages sent to the context will be sent to the global log handler too.
+ *
+ * If libusb is compiled without message logging or USE_SYSTEM_LOGGING_FACILITY
+ * is defined then global callback function will never be called.
+ * If ENABLE_DEBUG_LOGGING is defined then per context callback function will
+ * never be called.
+ *
+ * \param ctx context on which to assign log handler, or NULL for the default
+ * context. Parameter ignored if only LIBUSB_LOG_CB_GLOBAL mode is requested.
+ * \param cb pointer to the callback function, or NULL to stop log
+ * messages redirection
+ * \param mode mode of callback function operation. Several modes can be
+ * selected for a single callback function, see \ref libusb_log_cb_mode for
+ * a description.
+ * \see libusb_log_cb, libusb_log_cb_mode
+ */
+void API_EXPORTED libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb,
+       int mode)
+{
+#if defined(ENABLE_LOGGING) && (!defined(ENABLE_DEBUG_LOGGING) || !defined(USE_SYSTEM_LOGGING_FACILITY))
+#if !defined(USE_SYSTEM_LOGGING_FACILITY)
+       if (mode & LIBUSB_LOG_CB_GLOBAL)
+               log_handler = cb;
+#endif
+#if !defined(ENABLE_DEBUG_LOGGING)
+       if (mode & LIBUSB_LOG_CB_CONTEXT) {
+               ctx = usbi_get_context(ctx);
+               ctx->log_handler = cb;
+       }
+#else
+       UNUSED(ctx);
+#endif
+#else
+       UNUSED(ctx);
+       UNUSED(cb);
+       UNUSED(mode);
+#endif
+}
+
 /** \ingroup libusb_lib
  * Set an option in the library.
  *
@@ -2043,6 +2180,7 @@ void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level)
  * \returns LIBUSB_ERROR_INVALID_PARAM if the option or arguments are invalid
  * \returns LIBUSB_ERROR_NOT_SUPPORTED if the option is valid but not supported
  * on this platform
+ * \returns LIBUSB_ERROR_NOT_FOUND if LIBUSB_OPTION_USE_USBDK is valid on this platform but UsbDk is not available
  */
 int API_EXPORTED libusb_set_option(libusb_context *ctx,
        enum libusb_option option, ...)
@@ -2050,7 +2188,7 @@ int API_EXPORTED libusb_set_option(libusb_context *ctx,
        int arg, r = LIBUSB_SUCCESS;
        va_list ap;
 
-       USBI_GET_CONTEXT(ctx);
+       ctx = usbi_get_context(ctx);
 
        va_start(ap, option);
        switch (option) {
@@ -2068,6 +2206,7 @@ int API_EXPORTED libusb_set_option(libusb_context *ctx,
 
        /* Handle all backend-specific options here */
        case LIBUSB_OPTION_USE_USBDK:
+       case LIBUSB_OPTION_WEAK_AUTHORITY:
                if (usbi_backend.set_option)
                        r = usbi_backend.set_option(ctx, option, ap);
                else
@@ -2125,9 +2264,8 @@ int API_EXPORTED libusb_init(libusb_context **context)
 
        usbi_mutex_static_lock(&default_context_lock);
 
-       if (!timestamp_origin.tv_sec) {
-               usbi_backend.clock_gettime(USBI_CLOCK_REALTIME, &timestamp_origin);
-       }
+       if (!timestamp_origin.tv_sec)
+               usbi_get_monotonic_time(&timestamp_origin);
 
        if (!context && usbi_default_context) {
                usbi_dbg("reusing default context");
@@ -2136,7 +2274,7 @@ int API_EXPORTED libusb_init(libusb_context **context)
                return 0;
        }
 
-       ctx = calloc(1, sizeof(*ctx) + priv_size);
+       ctx = calloc(1, PTR_ALIGN(sizeof(*ctx)) + priv_size);
        if (!ctx) {
                r = LIBUSB_ERROR_NO_MEM;
                goto err_unlock;
@@ -2169,7 +2307,7 @@ int API_EXPORTED libusb_init(libusb_context **context)
        usbi_mutex_static_lock(&active_contexts_lock);
        if (first_init) {
                first_init = 0;
-               list_init (&active_contexts_list);
+               list_init(&active_contexts_list);
        }
        list_add (&ctx->list, &active_contexts_list);
        usbi_mutex_static_unlock(&active_contexts_lock);
@@ -2201,11 +2339,11 @@ err_free_ctx:
        }
 
        usbi_mutex_static_lock(&active_contexts_lock);
-       list_del (&ctx->list);
+       list_del(&ctx->list);
        usbi_mutex_static_unlock(&active_contexts_lock);
 
        usbi_mutex_lock(&ctx->usb_devs_lock);
-       list_for_each_entry_safe(dev, next, &ctx->usb_devs, list, struct libusb_device) {
+       for_each_device_safe(ctx, dev, next) {
                list_del(&dev->list);
                libusb_unref_device(dev);
        }
@@ -2226,30 +2364,47 @@ err_unlock:
  * before your application terminates.
  * \param ctx the context to deinitialize, or NULL for the default context
  */
-void API_EXPORTED libusb_exit(struct libusb_context *ctx)
+void API_EXPORTED libusb_exit(libusb_context *ctx)
 {
        struct libusb_device *dev, *next;
        struct timeval tv = { 0, 0 };
+       int destroying_default_context = 0;
 
-       usbi_dbg("");
-       USBI_GET_CONTEXT(ctx);
+       usbi_dbg(" ");
+
+       ctx = usbi_get_context(ctx);
 
        /* if working with default context, only actually do the deinitialization
         * if we're the last user */
        usbi_mutex_static_lock(&default_context_lock);
        if (ctx == usbi_default_context) {
+               if (!usbi_default_context) {
+                       usbi_dbg("no default context, not initialized?");
+                       usbi_mutex_static_unlock(&default_context_lock);
+                       return;
+               }
+
                if (--default_context_refcnt > 0) {
                        usbi_dbg("not destroying default context");
                        usbi_mutex_static_unlock(&default_context_lock);
                        return;
                }
                usbi_dbg("destroying default context");
-               usbi_default_context = NULL;
+
+               /*
+                * Setting this flag without unlocking the default context, as
+                * we are actually destroying the default context.
+                * usbi_default_context is not set to NULL yet, as all activities
+                * would only stop after usbi_backend->exit() returns.
+                */
+               destroying_default_context = 1;
+       } else {
+               /* Unlock default context, as we're not modifying it. */
+               usbi_mutex_static_unlock(&default_context_lock);
        }
-       usbi_mutex_static_unlock(&default_context_lock);
 
        usbi_mutex_static_lock(&active_contexts_lock);
-       list_del (&ctx->list);
+       list_del(&ctx->list);
        usbi_mutex_static_unlock(&active_contexts_lock);
 
        if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
@@ -2268,7 +2423,7 @@ void API_EXPORTED libusb_exit(struct libusb_context *ctx)
                        libusb_handle_events_timeout(ctx, &tv);
 
                usbi_mutex_lock(&ctx->usb_devs_lock);
-               list_for_each_entry_safe(dev, next, &ctx->usb_devs, list, struct libusb_device) {
+               for_each_device_safe(ctx, dev, next) {
                        list_del(&dev->list);
                        libusb_unref_device(dev);
                }
@@ -2290,6 +2445,11 @@ void API_EXPORTED libusb_exit(struct libusb_context *ctx)
        usbi_mutex_destroy(&ctx->usb_devs_lock);
        usbi_mutex_destroy(&ctx->hotplug_cbs_lock);
        free(ctx);
+
+       if (destroying_default_context) {
+               usbi_default_context = NULL;
+               usbi_mutex_static_unlock(&default_context_lock);
+       }
 }
 
 /** \ingroup libusb_misc
@@ -2321,89 +2481,90 @@ int API_EXPORTED libusb_has_capability(uint32_t capability)
 #ifdef LIBUSB_PRINTF_WIN32
 /*
  * Prior to VS2015, Microsoft did not provide the snprintf() function and
- * provided a vsnprintf() that did not guarantee NULL-terminated output.
+ * provided a vsnprintf() that did not guarantee NUL-terminated output.
  * Microsoft did provide a _snprintf() function, but again it did not
  * guarantee NULL-terminated output.
  *
- * The below implementations guarantee NULL-terminated output and are
+ * The below implementations guarantee NUL-terminated output and are
  * C99 compliant.
  */
 
 int usbi_snprintf(char *str, size_t size, const char *format, ...)
 {
-       va_list ap;
+       va_list args;
        int ret;
 
-       va_start(ap, format);
-       ret = usbi_vsnprintf(str, size, format, ap);
-       va_end(ap);
+       va_start(args, format);
+       ret = usbi_vsnprintf(str, size, format, args);
+       va_end(args);
 
        return ret;
 }
 
-int usbi_vsnprintf(char *str, size_t size, const char *format, va_list ap)
+int usbi_vsnprintf(char *str, size_t size, const char *format, va_list args)
 {
        int ret;
 
-       ret = _vsnprintf(str, size, format, ap);
+       ret = _vsnprintf(str, size, format, args);
        if (ret < 0 || ret == (int)size) {
-               /* Output is truncated, ensure buffer is NULL-terminated and
+               /* Output is truncated, ensure buffer is NUL-terminated and
                 * determine how many characters would have been written. */
                str[size - 1] = '\0';
                if (ret < 0)
-                       ret = _vsnprintf(NULL, 0, format, ap);
+                       ret = _vsnprintf(NULL, 0, format, args);
        }
 
        return ret;
 }
 #endif /* LIBUSB_PRINTF_WIN32 */
 
-static void usbi_log_str(enum libusb_log_level level, const char *str)
+static void log_str(enum libusb_log_level level, const char *str)
 {
 #if defined(USE_SYSTEM_LOGGING_FACILITY)
-#if defined(OS_WINDOWS)
-       OutputDebugString(str);
-#elif defined(OS_WINCE)
-       /* Windows CE only supports the Unicode version of OutputDebugString. */
-       WCHAR wbuf[USBI_MAX_LOG_LEN];
-       MultiByteToWideChar(CP_UTF8, 0, str, -1, wbuf, sizeof(wbuf));
-       OutputDebugStringW(wbuf);
-#elif defined(__ANDROID__)
-       int priority = ANDROID_LOG_UNKNOWN;
+#if defined(__ANDROID__)
+       int priority;
        switch (level) {
-       case LIBUSB_LOG_LEVEL_NONE: return;
+       case LIBUSB_LOG_LEVEL_NONE: return;     /* Impossible, but keeps compiler happy */
        case LIBUSB_LOG_LEVEL_ERROR: priority = ANDROID_LOG_ERROR; break;
        case LIBUSB_LOG_LEVEL_WARNING: priority = ANDROID_LOG_WARN; break;
        case LIBUSB_LOG_LEVEL_INFO: priority = ANDROID_LOG_INFO; break;
        case LIBUSB_LOG_LEVEL_DEBUG: priority = ANDROID_LOG_DEBUG; break;
+       default: priority = ANDROID_LOG_UNKNOWN;
        }
        __android_log_write(priority, "libusb", str);
-#elif defined(HAVE_SYSLOG_FUNC)
-       int syslog_level = LOG_INFO;
+#elif defined(_WIN32)
+       UNUSED(level);
+       OutputDebugStringA(str);
+#elif defined(HAVE_SYSLOG)
+       int syslog_level;
        switch (level) {
-       case LIBUSB_LOG_LEVEL_NONE: return;
+       case LIBUSB_LOG_LEVEL_NONE: return;     /* Impossible, but keeps compiler happy */
        case LIBUSB_LOG_LEVEL_ERROR: syslog_level = LOG_ERR; break;
        case LIBUSB_LOG_LEVEL_WARNING: syslog_level = LOG_WARNING; break;
        case LIBUSB_LOG_LEVEL_INFO: syslog_level = LOG_INFO; break;
        case LIBUSB_LOG_LEVEL_DEBUG: syslog_level = LOG_DEBUG; break;
+       default: syslog_level = LOG_INFO;
        }
        syslog(syslog_level, "%s", str);
-#else /* All of gcc, Clang, XCode seem to use #warning */
+#else /* All of gcc, Clang, Xcode seem to use #warning */
 #warning System logging is not supported on this platform. Logging to stderr will be used instead.
+       UNUSED(level);
        fputs(str, stderr);
 #endif
 #else
-       fputs(str, stderr);
+       /* Global log handler */
+       if (log_handler)
+               log_handler(NULL, level, str);
+       else
+               fputs(str, stderr);
 #endif /* USE_SYSTEM_LOGGING_FACILITY */
-       UNUSED(level);
 }
 
-void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
+static void log_v(struct libusb_context *ctx, enum libusb_log_level level,
        const char *function, const char *format, va_list args)
 {
        const char *prefix;
        char buf[USBI_MAX_LOG_LEN];
-       struct timespec now;
        int global_debug, header_len, text_len;
        static int has_debug_header_been_displayed = 0;
 
@@ -2411,41 +2572,22 @@ void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
        global_debug = 1;
        UNUSED(ctx);
 #else
-       enum libusb_log_level ctx_level = LIBUSB_LOG_LEVEL_NONE;
+       enum libusb_log_level ctx_level;
 
-       USBI_GET_CONTEXT(ctx);
+       ctx = usbi_get_context(ctx);
        if (ctx)
                ctx_level = ctx->debug;
        else
                ctx_level = get_env_debug_level();
 
-       if (ctx_level == LIBUSB_LOG_LEVEL_NONE)
-               return;
-       if (level == LIBUSB_LOG_LEVEL_WARNING && ctx_level < LIBUSB_LOG_LEVEL_WARNING)
-               return;
-       if (level == LIBUSB_LOG_LEVEL_INFO && ctx_level < LIBUSB_LOG_LEVEL_INFO)
-               return;
-       if (level == LIBUSB_LOG_LEVEL_DEBUG && ctx_level < LIBUSB_LOG_LEVEL_DEBUG)
+       if (ctx_level < level)
                return;
 
        global_debug = (ctx_level == LIBUSB_LOG_LEVEL_DEBUG);
 #endif
 
-       usbi_backend.clock_gettime(USBI_CLOCK_REALTIME, &now);
-       if ((global_debug) && (!has_debug_header_been_displayed)) {
-               has_debug_header_been_displayed = 1;
-               usbi_log_str(LIBUSB_LOG_LEVEL_DEBUG, "[timestamp] [threadID] facility level [function call] <message>" USBI_LOG_LINE_END);
-               usbi_log_str(LIBUSB_LOG_LEVEL_DEBUG, "--------------------------------------------------------------------------------" USBI_LOG_LINE_END);
-       }
-       if (now.tv_nsec < timestamp_origin.tv_nsec) {
-               now.tv_sec--;
-               now.tv_nsec += 1000000000L;
-       }
-       now.tv_sec -= timestamp_origin.tv_sec;
-       now.tv_nsec -= timestamp_origin.tv_nsec;
-
        switch (level) {
-       case LIBUSB_LOG_LEVEL_NONE:
+       case LIBUSB_LOG_LEVEL_NONE:     /* Impossible, but keeps compiler happy */
                return;
        case LIBUSB_LOG_LEVEL_ERROR:
                prefix = "error";
@@ -2465,35 +2607,51 @@ void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
        }
 
        if (global_debug) {
+               struct timespec timestamp;
+
+               if (!has_debug_header_been_displayed) {
+                       has_debug_header_been_displayed = 1;
+                       log_str(LIBUSB_LOG_LEVEL_DEBUG, "[timestamp] [threadID] facility level [function call] <message>" USBI_LOG_LINE_END);
+                       log_str(LIBUSB_LOG_LEVEL_DEBUG, "--------------------------------------------------------------------------------" USBI_LOG_LINE_END);
+               }
+
+               usbi_get_monotonic_time(&timestamp);
+               TIMESPEC_SUB(&timestamp, &timestamp_origin, &timestamp);
+
                header_len = snprintf(buf, sizeof(buf),
-                       "[%2d.%06d] [%08x] libusb: %s [%s] ",
-                       (int)now.tv_sec, (int)(now.tv_nsec / 1000L), usbi_get_tid(), prefix, function);
+                       "[%2ld.%06ld] [%08x] libusb: %s [%s] ",
+                       (long)timestamp.tv_sec, (long)(timestamp.tv_nsec / 1000L), usbi_get_tid(), prefix, function);
        } else {
                header_len = snprintf(buf, sizeof(buf),
                        "libusb: %s [%s] ", prefix, function);
        }
 
        if (header_len < 0 || header_len >= (int)sizeof(buf)) {
-               /* Somehow snprintf failed to write to the buffer,
+               /* Somehow snprintf() failed to write to the buffer,
                 * remove the header so something useful is output. */
                header_len = 0;
        }
-       /* Make sure buffer is NUL terminated */
-       buf[header_len] = '\0';
-       text_len = vsnprintf(buf + header_len, sizeof(buf) - header_len,
+
+       text_len = vsnprintf(buf + header_len, sizeof(buf) - (size_t)header_len,
                format, args);
        if (text_len < 0 || text_len + header_len >= (int)sizeof(buf)) {
                /* Truncated log output. On some platforms a -1 return value means
                 * that the output was truncated. */
-               text_len = sizeof(buf) - header_len;
+               text_len = (int)sizeof(buf) - header_len;
        }
-       if (header_len + text_len + sizeof(USBI_LOG_LINE_END) >= sizeof(buf)) {
+       if (header_len + text_len + (int)sizeof(USBI_LOG_LINE_END) >= (int)sizeof(buf)) {
                /* Need to truncate the text slightly to fit on the terminator. */
-               text_len -= (header_len + text_len + sizeof(USBI_LOG_LINE_END)) - sizeof(buf);
+               text_len -= (header_len + text_len + (int)sizeof(USBI_LOG_LINE_END)) - (int)sizeof(buf);
        }
        strcpy(buf + header_len + text_len, USBI_LOG_LINE_END);
 
-       usbi_log_str(level, buf);
+       log_str(level, buf);
+
+       /* Per-context log handler */
+#ifndef ENABLE_DEBUG_LOGGING
+       if (ctx && ctx->log_handler)
+               ctx->log_handler(ctx, level, buf);
+#endif
 }
 
 void usbi_log(struct libusb_context *ctx, enum libusb_log_level level,
@@ -2501,9 +2659,9 @@ void usbi_log(struct libusb_context *ctx, enum libusb_log_level level,
 {
        va_list args;
 
-       va_start (args, format);
-       usbi_log_v(ctx, level, function, format, args);
-       va_end (args);
+       va_start(args, format);
+       log_v(ctx, level, function, format, args);
+       va_end(args);
 }
 
 #endif /* ENABLE_LOGGING */
index 74d6de557eabdb2f6bbd97702cf27fff7834da8c..ecd9441804e6b3692dc25933f618592429e7384e 100644 (file)
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#include <config.h>
+#include "libusbi.h"
 
-#include <errno.h>
-#include <stdint.h>
-#include <stdlib.h>
 #include <string.h>
 
-#include "libusbi.h"
-
-#define DESC_HEADER_LENGTH             2
-#define DEVICE_DESC_LENGTH             18
-#define CONFIG_DESC_LENGTH             9
-#define INTERFACE_DESC_LENGTH          9
-#define ENDPOINT_DESC_LENGTH           7
-#define ENDPOINT_AUDIO_DESC_LENGTH     9
+#define DESC_HEADER_LENGTH     2
 
 /** @defgroup libusb_desc USB descriptors
  * This page details how to examine the various standard USB descriptors
  * for detected devices
  */
 
-/* set host_endian if the w values are already in host endian format,
- * as opposed to bus endian. */
-int usbi_parse_descriptor(const unsigned char *source, const char *descriptor,
-       void *dest, int host_endian)
+#define READ_LE16(p) ((uint16_t)       \
+       (((uint16_t)((p)[1]) << 8) |    \
+        ((uint16_t)((p)[0]))))
+
+#define READ_LE32(p) ((uint32_t)       \
+       (((uint32_t)((p)[3]) << 24) |   \
+        ((uint32_t)((p)[2]) << 16) |   \
+        ((uint32_t)((p)[1]) <<  8) |   \
+        ((uint32_t)((p)[0]))))
+
+static void parse_descriptor(const void *source, const char *descriptor, void *dest)
 {
-       const unsigned char *sp = source;
-       unsigned char *dp = dest;
-       uint16_t w;
-       const char *cp;
-       uint32_t d;
-
-       for (cp = descriptor; *cp; cp++) {
-               switch (*cp) {
-                       case 'b':       /* 8-bit byte */
-                               *dp++ = *sp++;
-                               break;
-                       case 'w':       /* 16-bit word, convert from little endian to CPU */
-                               dp += ((uintptr_t)dp & 1);      /* Align to word boundary */
-
-                               if (host_endian) {
-                                       memcpy(dp, sp, 2);
-                               } else {
-                                       w = (sp[1] << 8) | sp[0];
-                                       *((uint16_t *)dp) = w;
-                               }
-                               sp += 2;
-                               dp += 2;
-                               break;
-                       case 'd':       /* 32-bit word, convert from little endian to CPU */
-                               dp += ((uintptr_t)dp & 1);      /* Align to word boundary */
-
-                               if (host_endian) {
-                                       memcpy(dp, sp, 4);
-                               } else {
-                                       d = (sp[3] << 24) | (sp[2] << 16) |
-                                               (sp[1] << 8) | sp[0];
-                                       *((uint32_t *)dp) = d;
-                               }
-                               sp += 4;
-                               dp += 4;
-                               break;
-                       case 'u':       /* 16 byte UUID */
-                               memcpy(dp, sp, 16);
-                               sp += 16;
-                               dp += 16;
-                               break;
+       const uint8_t *sp = source;
+       uint8_t *dp = dest;
+       char field_type;
+
+       while (*descriptor) {
+               field_type = *descriptor++;
+               switch (field_type) {
+               case 'b':       /* 8-bit byte */
+                       *dp++ = *sp++;
+                       break;
+               case 'w':       /* 16-bit word, convert from little endian to CPU */
+                       dp += ((uintptr_t)dp & 1);      /* Align to 16-bit word boundary */
+
+                       *((uint16_t *)dp) = READ_LE16(sp);
+                       sp += 2;
+                       dp += 2;
+                       break;
+               case 'd':       /* 32-bit word, convert from little endian to CPU */
+                       dp += 4 - ((uintptr_t)dp & 3);  /* Align to 32-bit word boundary */
+
+                       *((uint32_t *)dp) = READ_LE32(sp);
+                       sp += 4;
+                       dp += 4;
+                       break;
+               case 'u':       /* 16 byte UUID */
+                       memcpy(dp, sp, 16);
+                       sp += 16;
+                       dp += 16;
+                       break;
                }
        }
-
-       return (int) (sp - source);
 }
 
 static void clear_endpoint(struct libusb_endpoint_descriptor *endpoint)
 {
-       free((void *) endpoint->extra);
+       free((void *)endpoint->extra);
 }
 
 static int parse_endpoint(struct libusb_context *ctx,
-       struct libusb_endpoint_descriptor *endpoint, unsigned char *buffer,
-       int size, int host_endian)
+       struct libusb_endpoint_descriptor *endpoint, const uint8_t *buffer, int size)
 {
-       struct usb_descriptor_header header;
-       unsigned char *extra;
-       unsigned char *begin;
+       const struct usbi_descriptor_header *header;
+       const uint8_t *begin;
+       void *extra;
        int parsed = 0;
        int len;
 
@@ -113,75 +95,69 @@ static int parse_endpoint(struct libusb_context *ctx,
                return LIBUSB_ERROR_IO;
        }
 
-       usbi_parse_descriptor(buffer, "bb", &header, 0);
-       if (header.bDescriptorType != LIBUSB_DT_ENDPOINT) {
-               usbi_err(ctx, "unexpected descriptor %x (expected %x)",
-                       header.bDescriptorType, LIBUSB_DT_ENDPOINT);
+       header = (const struct usbi_descriptor_header *)buffer;
+       if (header->bDescriptorType != LIBUSB_DT_ENDPOINT) {
+               usbi_err(ctx, "unexpected descriptor 0x%x (expected 0x%x)",
+                       header->bDescriptorType, LIBUSB_DT_ENDPOINT);
                return parsed;
-       }
-       if (header.bLength > size) {
-               usbi_warn(ctx, "short endpoint descriptor read %d/%d",
-                         size, header.bLength);
-               return parsed;
-       }
-       if (header.bLength >= ENDPOINT_AUDIO_DESC_LENGTH)
-               usbi_parse_descriptor(buffer, "bbbbwbbb", endpoint, host_endian);
-       else if (header.bLength >= ENDPOINT_DESC_LENGTH)
-               usbi_parse_descriptor(buffer, "bbbbwb", endpoint, host_endian);
-       else {
-               usbi_err(ctx, "invalid endpoint bLength (%d)", header.bLength);
+       } else if (header->bLength < LIBUSB_DT_ENDPOINT_SIZE) {
+               usbi_err(ctx, "invalid endpoint bLength (%u)", header->bLength);
                return LIBUSB_ERROR_IO;
+       } else if (header->bLength > size) {
+               usbi_warn(ctx, "short endpoint descriptor read %d/%u",
+                         size, header->bLength);
+               return parsed;
        }
 
-       buffer += header.bLength;
-       size -= header.bLength;
-       parsed += header.bLength;
+       if (header->bLength >= LIBUSB_DT_ENDPOINT_AUDIO_SIZE)
+               parse_descriptor(buffer, "bbbbwbbb", endpoint);
+       else
+               parse_descriptor(buffer, "bbbbwb", endpoint);
+
+       buffer += header->bLength;
+       size -= header->bLength;
+       parsed += header->bLength;
 
        /* Skip over the rest of the Class Specific or Vendor Specific */
        /*  descriptors */
        begin = buffer;
        while (size >= DESC_HEADER_LENGTH) {
-               usbi_parse_descriptor(buffer, "bb", &header, 0);
-               if (header.bLength < DESC_HEADER_LENGTH) {
-                       usbi_err(ctx, "invalid extra ep desc len (%d)",
-                                header.bLength);
+               header = (const struct usbi_descriptor_header *)buffer;
+               if (header->bLength < DESC_HEADER_LENGTH) {
+                       usbi_err(ctx, "invalid extra ep desc len (%u)",
+                                header->bLength);
                        return LIBUSB_ERROR_IO;
-               } else if (header.bLength > size) {
-                       usbi_warn(ctx, "short extra ep desc read %d/%d",
-                                 size, header.bLength);
+               } else if (header->bLength > size) {
+                       usbi_warn(ctx, "short extra ep desc read %d/%u",
+                                 size, header->bLength);
                        return parsed;
                }
 
                /* If we find another "proper" descriptor then we're done  */
-               if ((header.bDescriptorType == LIBUSB_DT_ENDPOINT) ||
-                               (header.bDescriptorType == LIBUSB_DT_INTERFACE) ||
-                               (header.bDescriptorType == LIBUSB_DT_CONFIG) ||
-                               (header.bDescriptorType == LIBUSB_DT_DEVICE))
+               if (header->bDescriptorType == LIBUSB_DT_ENDPOINT ||
+                   header->bDescriptorType == LIBUSB_DT_INTERFACE ||
+                   header->bDescriptorType == LIBUSB_DT_CONFIG ||
+                   header->bDescriptorType == LIBUSB_DT_DEVICE)
                        break;
 
-               usbi_dbg("skipping descriptor %x", header.bDescriptorType);
-               buffer += header.bLength;
-               size -= header.bLength;
-               parsed += header.bLength;
+               usbi_dbg("skipping descriptor 0x%x", header->bDescriptorType);
+               buffer += header->bLength;
+               size -= header->bLength;
+               parsed += header->bLength;
        }
 
        /* Copy any unknown descriptors into a storage area for drivers */
        /*  to later parse */
        len = (int)(buffer - begin);
-       if (!len) {
-               endpoint->extra = NULL;
-               endpoint->extra_length = 0;
+       if (len <= 0)
                return parsed;
-       }
 
-       extra = malloc(len);
-       endpoint->extra = extra;
-       if (!extra) {
-               endpoint->extra_length = 0;
+       extra = malloc((size_t)len);
+       if (!extra)
                return LIBUSB_ERROR_NO_MEM;
-       }
 
        memcpy(extra, begin, len);
+       endpoint->extra = extra;
        endpoint->extra_length = len;
 
        return parsed;
@@ -190,47 +166,45 @@ static int parse_endpoint(struct libusb_context *ctx,
 static void clear_interface(struct libusb_interface *usb_interface)
 {
        int i;
-       int j;
 
        if (usb_interface->altsetting) {
                for (i = 0; i < usb_interface->num_altsetting; i++) {
                        struct libusb_interface_descriptor *ifp =
                                (struct libusb_interface_descriptor *)
                                usb_interface->altsetting + i;
-                       free((void *) ifp->extra);
+
+                       free((void *)ifp->extra);
                        if (ifp->endpoint) {
+                               uint8_t j;
+
                                for (j = 0; j < ifp->bNumEndpoints; j++)
                                        clear_endpoint((struct libusb_endpoint_descriptor *)
                                                       ifp->endpoint + j);
                        }
-                       free((void *) ifp->endpoint);
+                       free((void *)ifp->endpoint);
                }
        }
-       free((void *) usb_interface->altsetting);
+       free((void *)usb_interface->altsetting);
        usb_interface->altsetting = NULL;
 }
 
 static int parse_interface(libusb_context *ctx,
-       struct libusb_interface *usb_interface, unsigned char *buffer, int size,
-       int host_endian)
+       struct libusb_interface *usb_interface, const uint8_t *buffer, int size)
 {
-       int i;
        int len;
        int r;
        int parsed = 0;
        int interface_number = -1;
-       struct usb_descriptor_header header;
+       const struct usbi_descriptor_header *header;
+       const struct usbi_interface_descriptor *if_desc;
        struct libusb_interface_descriptor *ifp;
-       unsigned char *begin;
+       const uint8_t *begin;
 
-       usb_interface->num_altsetting = 0;
+       while (size >= LIBUSB_DT_INTERFACE_SIZE) {
+               struct libusb_interface_descriptor *altsetting;
 
-       while (size >= INTERFACE_DESC_LENGTH) {
-               struct libusb_interface_descriptor *altsetting =
-                       (struct libusb_interface_descriptor *) usb_interface->altsetting;
-               altsetting = usbi_reallocf(altsetting,
-                       sizeof(struct libusb_interface_descriptor) *
-                       (usb_interface->num_altsetting + 1));
+               altsetting = realloc((void *)usb_interface->altsetting,
+                       sizeof(*altsetting) * (size_t)(usb_interface->num_altsetting + 1));
                if (!altsetting) {
                        r = LIBUSB_ERROR_NO_MEM;
                        goto err;
@@ -238,25 +212,22 @@ static int parse_interface(libusb_context *ctx,
                usb_interface->altsetting = altsetting;
 
                ifp = altsetting + usb_interface->num_altsetting;
-               usbi_parse_descriptor(buffer, "bbbbbbbbb", ifp, 0);
+               parse_descriptor(buffer, "bbbbbbbbb", ifp);
                if (ifp->bDescriptorType != LIBUSB_DT_INTERFACE) {
-                       usbi_err(ctx, "unexpected descriptor %x (expected %x)",
+                       usbi_err(ctx, "unexpected descriptor 0x%x (expected 0x%x)",
                                 ifp->bDescriptorType, LIBUSB_DT_INTERFACE);
                        return parsed;
-               }
-               if (ifp->bLength < INTERFACE_DESC_LENGTH) {
-                       usbi_err(ctx, "invalid interface bLength (%d)",
+               } else if (ifp->bLength < LIBUSB_DT_INTERFACE_SIZE) {
+                       usbi_err(ctx, "invalid interface bLength (%u)",
                                 ifp->bLength);
                        r = LIBUSB_ERROR_IO;
                        goto err;
-               }
-               if (ifp->bLength > size) {
-                       usbi_warn(ctx, "short intf descriptor read %d/%d",
+               } else if (ifp->bLength > size) {
+                       usbi_warn(ctx, "short intf descriptor read %d/%u",
                                 size, ifp->bLength);
                        return parsed;
-               }
-               if (ifp->bNumEndpoints > USB_MAXENDPOINTS) {
-                       usbi_err(ctx, "too many endpoints (%d)", ifp->bNumEndpoints);
+               } else if (ifp->bNumEndpoints > USB_MAXENDPOINTS) {
+                       usbi_err(ctx, "too many endpoints (%u)", ifp->bNumEndpoints);
                        r = LIBUSB_ERROR_IO;
                        goto err;
                }
@@ -278,61 +249,65 @@ static int parse_interface(libusb_context *ctx,
 
                /* Skip over any interface, class or vendor descriptors */
                while (size >= DESC_HEADER_LENGTH) {
-                       usbi_parse_descriptor(buffer, "bb", &header, 0);
-                       if (header.bLength < DESC_HEADER_LENGTH) {
+                       header = (const struct usbi_descriptor_header *)buffer;
+                       if (header->bLength < DESC_HEADER_LENGTH) {
                                usbi_err(ctx,
-                                        "invalid extra intf desc len (%d)",
-                                        header.bLength);
+                                        "invalid extra intf desc len (%u)",
+                                        header->bLength);
                                r = LIBUSB_ERROR_IO;
                                goto err;
-                       } else if (header.bLength > size) {
+                       } else if (header->bLength > size) {
                                usbi_warn(ctx,
-                                         "short extra intf desc read %d/%d",
-                                         size, header.bLength);
+                                         "short extra intf desc read %d/%u",
+                                         size, header->bLength);
                                return parsed;
                        }
 
                        /* If we find another "proper" descriptor then we're done */
-                       if ((header.bDescriptorType == LIBUSB_DT_INTERFACE) ||
-                                       (header.bDescriptorType == LIBUSB_DT_ENDPOINT) ||
-                                       (header.bDescriptorType == LIBUSB_DT_CONFIG) ||
-                                       (header.bDescriptorType == LIBUSB_DT_DEVICE))
+                       if (header->bDescriptorType == LIBUSB_DT_INTERFACE ||
+                           header->bDescriptorType == LIBUSB_DT_ENDPOINT ||
+                           header->bDescriptorType == LIBUSB_DT_CONFIG ||
+                           header->bDescriptorType == LIBUSB_DT_DEVICE)
                                break;
 
-                       buffer += header.bLength;
-                       parsed += header.bLength;
-                       size -= header.bLength;
+                       buffer += header->bLength;
+                       parsed += header->bLength;
+                       size -= header->bLength;
                }
 
                /* Copy any unknown descriptors into a storage area for */
                /*  drivers to later parse */
                len = (int)(buffer - begin);
-               if (len) {
-                       ifp->extra = malloc(len);
-                       if (!ifp->extra) {
+               if (len > 0) {
+                       void *extra = malloc((size_t)len);
+
+                       if (!extra) {
                                r = LIBUSB_ERROR_NO_MEM;
                                goto err;
                        }
-                       memcpy((unsigned char *) ifp->extra, begin, len);
+
+                       memcpy(extra, begin, len);
+                       ifp->extra = extra;
                        ifp->extra_length = len;
                }
 
                if (ifp->bNumEndpoints > 0) {
                        struct libusb_endpoint_descriptor *endpoint;
-                       endpoint = calloc(ifp->bNumEndpoints, sizeof(struct libusb_endpoint_descriptor));
-                       ifp->endpoint = endpoint;
+                       uint8_t i;
+
+                       endpoint = calloc(ifp->bNumEndpoints, sizeof(*endpoint));
                        if (!endpoint) {
                                r = LIBUSB_ERROR_NO_MEM;
                                goto err;
                        }
 
+                       ifp->endpoint = endpoint;
                        for (i = 0; i < ifp->bNumEndpoints; i++) {
-                               r = parse_endpoint(ctx, endpoint + i, buffer, size,
-                                       host_endian);
+                               r = parse_endpoint(ctx, endpoint + i, buffer, size);
                                if (r < 0)
                                        goto err;
                                if (r == 0) {
-                                       ifp->bNumEndpoints = (uint8_t)i;
+                                       ifp->bNumEndpoints = i;
                                        break;
                                }
 
@@ -343,10 +318,10 @@ static int parse_interface(libusb_context *ctx,
                }
 
                /* We check to see if it's an alternate to this one */
-               ifp = (struct libusb_interface_descriptor *) buffer;
+               if_desc = (const struct usbi_interface_descriptor *)buffer;
                if (size < LIBUSB_DT_INTERFACE_SIZE ||
-                               ifp->bDescriptorType != LIBUSB_DT_INTERFACE ||
-                               ifp->bInterfaceNumber != interface_number)
+                   if_desc->bDescriptorType != LIBUSB_DT_INTERFACE ||
+                   if_desc->bInterfaceNumber != interface_number)
                        return parsed;
        }
 
@@ -358,23 +333,23 @@ err:
 
 static void clear_configuration(struct libusb_config_descriptor *config)
 {
-       int i;
+       uint8_t i;
+
        if (config->interface) {
                for (i = 0; i < config->bNumInterfaces; i++)
                        clear_interface((struct libusb_interface *)
                                        config->interface + i);
        }
-       free((void *) config->interface);
-       free((void *) config->extra);
+       free((void *)config->interface);
+       free((void *)config->extra);
 }
 
 static int parse_configuration(struct libusb_context *ctx,
-       struct libusb_config_descriptor *config, unsigned char *buffer,
-       int size, int host_endian)
+       struct libusb_config_descriptor *config, const uint8_t *buffer, int size)
 {
-       int i;
+       uint8_t i;
        int r;
-       struct usb_descriptor_header header;
+       const struct usbi_descriptor_header *header;
        struct libusb_interface *usb_interface;
 
        if (size < LIBUSB_DT_CONFIG_SIZE) {
@@ -383,95 +358,89 @@ static int parse_configuration(struct libusb_context *ctx,
                return LIBUSB_ERROR_IO;
        }
 
-       usbi_parse_descriptor(buffer, "bbwbbbbb", config, host_endian);
+       parse_descriptor(buffer, "bbwbbbbb", config);
        if (config->bDescriptorType != LIBUSB_DT_CONFIG) {
-               usbi_err(ctx, "unexpected descriptor %x (expected %x)",
+               usbi_err(ctx, "unexpected descriptor 0x%x (expected 0x%x)",
                         config->bDescriptorType, LIBUSB_DT_CONFIG);
                return LIBUSB_ERROR_IO;
-       }
-       if (config->bLength < LIBUSB_DT_CONFIG_SIZE) {
-               usbi_err(ctx, "invalid config bLength (%d)", config->bLength);
+       } else if (config->bLength < LIBUSB_DT_CONFIG_SIZE) {
+               usbi_err(ctx, "invalid config bLength (%u)", config->bLength);
                return LIBUSB_ERROR_IO;
-       }
-       if (config->bLength > size) {
-               usbi_err(ctx, "short config descriptor read %d/%d",
+       } else if (config->bLength > size) {
+               usbi_err(ctx, "short config descriptor read %d/%u",
                         size, config->bLength);
                return LIBUSB_ERROR_IO;
-       }
-       if (config->bNumInterfaces > USB_MAXINTERFACES) {
-               usbi_err(ctx, "too many interfaces (%d)", config->bNumInterfaces);
+       } else if (config->bNumInterfaces > USB_MAXINTERFACES) {
+               usbi_err(ctx, "too many interfaces (%u)", config->bNumInterfaces);
                return LIBUSB_ERROR_IO;
        }
 
-       usb_interface = calloc(config->bNumInterfaces, sizeof(struct libusb_interface));
-       config->interface = usb_interface;
+       usb_interface = calloc(config->bNumInterfaces, sizeof(*usb_interface));
        if (!usb_interface)
                return LIBUSB_ERROR_NO_MEM;
 
+       config->interface = usb_interface;
+
        buffer += config->bLength;
        size -= config->bLength;
 
-       config->extra = NULL;
-       config->extra_length = 0;
-
        for (i = 0; i < config->bNumInterfaces; i++) {
                int len;
-               unsigned char *begin;
+               const uint8_t *begin;
 
                /* Skip over the rest of the Class Specific or Vendor */
                /*  Specific descriptors */
                begin = buffer;
                while (size >= DESC_HEADER_LENGTH) {
-                       usbi_parse_descriptor(buffer, "bb", &header, 0);
-
-                       if (header.bLength < DESC_HEADER_LENGTH) {
+                       header = (const struct usbi_descriptor_header *)buffer;
+                       if (header->bLength < DESC_HEADER_LENGTH) {
                                usbi_err(ctx,
-                                        "invalid extra config desc len (%d)",
-                                        header.bLength);
+                                        "invalid extra config desc len (%u)",
+                                        header->bLength);
                                r = LIBUSB_ERROR_IO;
                                goto err;
-                       } else if (header.bLength > size) {
+                       } else if (header->bLength > size) {
                                usbi_warn(ctx,
-                                         "short extra config desc read %d/%d",
-                                         size, header.bLength);
-                               config->bNumInterfaces = (uint8_t)i;
+                                         "short extra config desc read %d/%u",
+                                         size, header->bLength);
+                               config->bNumInterfaces = i;
                                return size;
                        }
 
                        /* If we find another "proper" descriptor then we're done */
-                       if ((header.bDescriptorType == LIBUSB_DT_ENDPOINT) ||
-                                       (header.bDescriptorType == LIBUSB_DT_INTERFACE) ||
-                                       (header.bDescriptorType == LIBUSB_DT_CONFIG) ||
-                                       (header.bDescriptorType == LIBUSB_DT_DEVICE))
+                       if (header->bDescriptorType == LIBUSB_DT_ENDPOINT ||
+                           header->bDescriptorType == LIBUSB_DT_INTERFACE ||
+                           header->bDescriptorType == LIBUSB_DT_CONFIG ||
+                           header->bDescriptorType == LIBUSB_DT_DEVICE)
                                break;
 
-                       usbi_dbg("skipping descriptor 0x%x", header.bDescriptorType);
-                       buffer += header.bLength;
-                       size -= header.bLength;
+                       usbi_dbg("skipping descriptor 0x%x", header->bDescriptorType);
+                       buffer += header->bLength;
+                       size -= header->bLength;
                }
 
                /* Copy any unknown descriptors into a storage area for */
                /*  drivers to later parse */
                len = (int)(buffer - begin);
-               if (len) {
-                       /* FIXME: We should realloc and append here */
-                       if (!config->extra_length) {
-                               config->extra = malloc(len);
-                               if (!config->extra) {
-                                       r = LIBUSB_ERROR_NO_MEM;
-                                       goto err;
-                               }
+               if (len > 0) {
+                       uint8_t *extra = realloc((void *)config->extra,
+                                                (size_t)(config->extra_length + len));
 
-                               memcpy((unsigned char *) config->extra, begin, len);
-                               config->extra_length = len;
+                       if (!extra) {
+                               r = LIBUSB_ERROR_NO_MEM;
+                               goto err;
                        }
+
+                       memcpy(extra + config->extra_length, begin, len);
+                       config->extra = extra;
+                       config->extra_length += len;
                }
 
-               r = parse_interface(ctx, usb_interface + i, buffer, size, host_endian);
+               r = parse_interface(ctx, usb_interface + i, buffer, size);
                if (r < 0)
                        goto err;
                if (r == 0) {
-                       config->bNumInterfaces = (uint8_t)i;
+                       config->bNumInterfaces = i;
                        break;
                }
 
@@ -487,16 +456,15 @@ err:
 }
 
 static int raw_desc_to_config(struct libusb_context *ctx,
-       unsigned char *buf, int size, int host_endian,
-       struct libusb_config_descriptor **config)
+       const uint8_t *buf, int size, struct libusb_config_descriptor **config)
 {
-       struct libusb_config_descriptor *_config = malloc(sizeof(*_config));
+       struct libusb_config_descriptor *_config = calloc(1, sizeof(*_config));
        int r;
-       
+
        if (!_config)
                return LIBUSB_ERROR_NO_MEM;
 
-       r = parse_configuration(ctx, _config, buf, size, host_endian);
+       r = parse_configuration(ctx, _config, buf, size);
        if (r < 0) {
                usbi_err(ctx, "parse_configuration failed with error %d", r);
                free(_config);
@@ -504,28 +472,48 @@ static int raw_desc_to_config(struct libusb_context *ctx,
        } else if (r > 0) {
                usbi_warn(ctx, "still %d bytes of descriptor data left", r);
        }
-       
+
        *config = _config;
        return LIBUSB_SUCCESS;
 }
 
-int usbi_device_cache_descriptor(libusb_device *dev)
+static int get_active_config_descriptor(struct libusb_device *dev,
+       uint8_t *buffer, size_t size)
 {
-       int r, host_endian = 0;
+       int r = usbi_backend.get_active_config_descriptor(dev, buffer, size);
 
-       r = usbi_backend.get_device_descriptor(dev, (unsigned char *) &dev->device_descriptor,
-                                               &host_endian);
        if (r < 0)
                return r;
 
-       if (!host_endian) {
-               dev->device_descriptor.bcdUSB = libusb_le16_to_cpu(dev->device_descriptor.bcdUSB);
-               dev->device_descriptor.idVendor = libusb_le16_to_cpu(dev->device_descriptor.idVendor);
-               dev->device_descriptor.idProduct = libusb_le16_to_cpu(dev->device_descriptor.idProduct);
-               dev->device_descriptor.bcdDevice = libusb_le16_to_cpu(dev->device_descriptor.bcdDevice);
+       if (r < LIBUSB_DT_CONFIG_SIZE) {
+               usbi_err(DEVICE_CTX(dev), "short config descriptor read %d/%d",
+                        r, LIBUSB_DT_CONFIG_SIZE);
+               return LIBUSB_ERROR_IO;
+       } else if (r != (int)size) {
+               usbi_warn(DEVICE_CTX(dev), "short config descriptor read %d/%d",
+                        r, (int)size);
        }
 
-       return LIBUSB_SUCCESS;
+       return r;
+}
+
+static int get_config_descriptor(struct libusb_device *dev, uint8_t config_idx,
+       uint8_t *buffer, size_t size)
+{
+       int r = usbi_backend.get_config_descriptor(dev, config_idx, buffer, size);
+
+       if (r < 0)
+               return r;
+       if (r < LIBUSB_DT_CONFIG_SIZE) {
+               usbi_err(DEVICE_CTX(dev), "short config descriptor read %d/%d",
+                        r, LIBUSB_DT_CONFIG_SIZE);
+               return LIBUSB_ERROR_IO;
+       } else if (r != (int)size) {
+               usbi_warn(DEVICE_CTX(dev), "short config descriptor read %d/%d",
+                        r, (int)size);
+       }
+
+       return r;
 }
 
 /** \ingroup libusb_desc
@@ -543,9 +531,10 @@ int usbi_device_cache_descriptor(libusb_device *dev)
 int API_EXPORTED libusb_get_device_descriptor(libusb_device *dev,
        struct libusb_device_descriptor *desc)
 {
-       usbi_dbg("");
-       memcpy((unsigned char *) desc, (unsigned char *) &dev->device_descriptor,
-              sizeof (dev->device_descriptor));
+       usbi_dbg(" ");
+       static_assert(sizeof(dev->device_descriptor) == LIBUSB_DT_DEVICE_SIZE,
+                     "struct libusb_device_descriptor is not expected size");
+       *desc = dev->device_descriptor;
        return 0;
 }
 
@@ -566,31 +555,23 @@ int API_EXPORTED libusb_get_device_descriptor(libusb_device *dev,
 int API_EXPORTED libusb_get_active_config_descriptor(libusb_device *dev,
        struct libusb_config_descriptor **config)
 {
-       struct libusb_config_descriptor _config;
-       unsigned char tmp[LIBUSB_DT_CONFIG_SIZE];
-       unsigned char *buf = NULL;
-       int host_endian = 0;
+       union usbi_config_desc_buf _config;
+       uint16_t config_len;
+       uint8_t *buf;
        int r;
 
-       r = usbi_backend.get_active_config_descriptor(dev, tmp,
-               LIBUSB_DT_CONFIG_SIZE, &host_endian);
+       r = get_active_config_descriptor(dev, _config.buf, sizeof(_config.buf));
        if (r < 0)
                return r;
-       if (r < LIBUSB_DT_CONFIG_SIZE) {
-               usbi_err(dev->ctx, "short config descriptor read %d/%d",
-                        r, LIBUSB_DT_CONFIG_SIZE);
-               return LIBUSB_ERROR_IO;
-       }
 
-       usbi_parse_descriptor(tmp, "bbw", &_config, host_endian);
-       buf = malloc(_config.wTotalLength);
+       config_len = libusb_le16_to_cpu(_config.desc.wTotalLength);
+       buf = malloc(config_len);
        if (!buf)
                return LIBUSB_ERROR_NO_MEM;
 
-       r = usbi_backend.get_active_config_descriptor(dev, buf,
-               _config.wTotalLength, &host_endian);
+       r = get_active_config_descriptor(dev, buf, config_len);
        if (r >= 0)
-               r = raw_desc_to_config(dev->ctx, buf, r, host_endian, config);
+               r = raw_desc_to_config(DEVICE_CTX(dev), buf, r, config);
 
        free(buf);
        return r;
@@ -615,70 +596,32 @@ int API_EXPORTED libusb_get_active_config_descriptor(libusb_device *dev,
 int API_EXPORTED libusb_get_config_descriptor(libusb_device *dev,
        uint8_t config_index, struct libusb_config_descriptor **config)
 {
-       struct libusb_config_descriptor _config;
-       unsigned char tmp[LIBUSB_DT_CONFIG_SIZE];
-       unsigned char *buf = NULL;
-       int host_endian = 0;
+       union usbi_config_desc_buf _config;
+       uint16_t config_len;
+       uint8_t *buf;
        int r;
 
-       usbi_dbg("index %d", config_index);
-       if (config_index >= dev->num_configurations)
+       usbi_dbg("index %u", config_index);
+       if (config_index >= dev->device_descriptor.bNumConfigurations)
                return LIBUSB_ERROR_NOT_FOUND;
 
-       r = usbi_backend.get_config_descriptor(dev, config_index, tmp,
-               LIBUSB_DT_CONFIG_SIZE, &host_endian);
+       r = get_config_descriptor(dev, config_index, _config.buf, sizeof(_config.buf));
        if (r < 0)
                return r;
-       if (r < LIBUSB_DT_CONFIG_SIZE) {
-               usbi_err(dev->ctx, "short config descriptor read %d/%d",
-                        r, LIBUSB_DT_CONFIG_SIZE);
-               return LIBUSB_ERROR_IO;
-       }
 
-       usbi_parse_descriptor(tmp, "bbw", &_config, host_endian);
-       buf = malloc(_config.wTotalLength);
+       config_len = libusb_le16_to_cpu(_config.desc.wTotalLength);
+       buf = malloc(config_len);
        if (!buf)
                return LIBUSB_ERROR_NO_MEM;
 
-       r = usbi_backend.get_config_descriptor(dev, config_index, buf,
-               _config.wTotalLength, &host_endian);
+       r = get_config_descriptor(dev, config_index, buf, config_len);
        if (r >= 0)
-               r = raw_desc_to_config(dev->ctx, buf, r, host_endian, config);
+               r = raw_desc_to_config(DEVICE_CTX(dev), buf, r, config);
 
        free(buf);
        return r;
 }
 
-/* iterate through all configurations, returning the index of the configuration
- * matching a specific bConfigurationValue in the idx output parameter, or -1
- * if the config was not found.
- * returns 0 on success or a LIBUSB_ERROR code
- */
-int usbi_get_config_index_by_value(struct libusb_device *dev,
-       uint8_t bConfigurationValue, int *idx)
-{
-       uint8_t i;
-
-       usbi_dbg("value %d", bConfigurationValue);
-       for (i = 0; i < dev->num_configurations; i++) {
-               unsigned char tmp[6];
-               int host_endian;
-               int r = usbi_backend.get_config_descriptor(dev, i, tmp, sizeof(tmp),
-                       &host_endian);
-               if (r < 0) {
-                       *idx = -1;
-                       return r;
-               }
-               if (tmp[5] == bConfigurationValue) {
-                       *idx = i;
-                       return 0;
-               }
-       }
-
-       *idx = -1;
-       return 0;
-}
-
 /** \ingroup libusb_desc
  * Get a USB configuration descriptor with a specific bConfigurationValue.
  * This is a non-blocking function which does not involve any requests being
@@ -699,24 +642,33 @@ int usbi_get_config_index_by_value(struct libusb_device *dev,
 int API_EXPORTED libusb_get_config_descriptor_by_value(libusb_device *dev,
        uint8_t bConfigurationValue, struct libusb_config_descriptor **config)
 {
-       int r, idx, host_endian;
-       unsigned char *buf = NULL;
+       uint8_t idx;
+       int r;
 
        if (usbi_backend.get_config_descriptor_by_value) {
+               void *buf;
+
                r = usbi_backend.get_config_descriptor_by_value(dev,
-                       bConfigurationValue, &buf, &host_endian);
+                       bConfigurationValue, &buf);
                if (r < 0)
                        return r;
-               return raw_desc_to_config(dev->ctx, buf, r, host_endian, config);
+
+               return raw_desc_to_config(DEVICE_CTX(dev), buf, r, config);
        }
 
-       r = usbi_get_config_index_by_value(dev, bConfigurationValue, &idx);
-       if (r < 0)
-               return r;
-       else if (idx == -1)
-               return LIBUSB_ERROR_NOT_FOUND;
-       else
-               return libusb_get_config_descriptor(dev, (uint8_t) idx, config);
+       usbi_dbg("value %u", bConfigurationValue);
+       for (idx = 0; idx < dev->device_descriptor.bNumConfigurations; idx++) {
+               union usbi_config_desc_buf _config;
+
+               r = get_config_descriptor(dev, idx, _config.buf, sizeof(_config.buf));
+               if (r < 0)
+                       return r;
+
+               if (_config.desc.bConfigurationValue == bConfigurationValue)
+                       return libusb_get_config_descriptor(dev, idx, config);
+       }
+
+       return LIBUSB_ERROR_NOT_FOUND;
 }
 
 /** \ingroup libusb_desc
@@ -751,37 +703,41 @@ void API_EXPORTED libusb_free_config_descriptor(
  * \returns another LIBUSB_ERROR code on error
  */
 int API_EXPORTED libusb_get_ss_endpoint_companion_descriptor(
-       struct libusb_context *ctx,
+       libusb_context *ctx,
        const struct libusb_endpoint_descriptor *endpoint,
        struct libusb_ss_endpoint_companion_descriptor **ep_comp)
 {
-       struct usb_descriptor_header header;
+       struct usbi_descriptor_header *header;
+       const uint8_t *buffer = endpoint->extra;
        int size = endpoint->extra_length;
-       const unsigned char *buffer = endpoint->extra;
 
        *ep_comp = NULL;
 
        while (size >= DESC_HEADER_LENGTH) {
-               usbi_parse_descriptor(buffer, "bb", &header, 0);
-               if (header.bLength < 2 || header.bLength > size) {
-                       usbi_err(ctx, "invalid descriptor length %d",
-                                header.bLength);
-                       return LIBUSB_ERROR_IO;
-               }
-               if (header.bDescriptorType != LIBUSB_DT_SS_ENDPOINT_COMPANION) {
-                       buffer += header.bLength;
-                       size -= header.bLength;
+               header = (struct usbi_descriptor_header *)buffer;
+               if (header->bDescriptorType != LIBUSB_DT_SS_ENDPOINT_COMPANION) {
+                       if (header->bLength < DESC_HEADER_LENGTH) {
+                               usbi_err(ctx, "invalid descriptor length %u",
+                                        header->bLength);
+                               return LIBUSB_ERROR_IO;
+                       }
+                       buffer += header->bLength;
+                       size -= header->bLength;
                        continue;
-               }
-               if (header.bLength < LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE) {
-                       usbi_err(ctx, "invalid ss-ep-comp-desc length %d",
-                                header.bLength);
+               } else if (header->bLength < LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE) {
+                       usbi_err(ctx, "invalid ss-ep-comp-desc length %u",
+                                header->bLength);
+                       return LIBUSB_ERROR_IO;
+               } else if (header->bLength > size) {
+                       usbi_err(ctx, "short ss-ep-comp-desc read %d/%u",
+                                size, header->bLength);
                        return LIBUSB_ERROR_IO;
                }
+
                *ep_comp = malloc(sizeof(**ep_comp));
-               if (*ep_comp == NULL)
+               if (!*ep_comp)
                        return LIBUSB_ERROR_NO_MEM;
-               usbi_parse_descriptor(buffer, "bbbbw", *ep_comp, 0);
+               parse_descriptor(buffer, "bbbbw", *ep_comp);
                return LIBUSB_SUCCESS;
        }
        return LIBUSB_ERROR_NOT_FOUND;
@@ -803,11 +759,12 @@ void API_EXPORTED libusb_free_ss_endpoint_companion_descriptor(
 
 static int parse_bos(struct libusb_context *ctx,
        struct libusb_bos_descriptor **bos,
-       unsigned char *buffer, int size, int host_endian)
+       const uint8_t *buffer, int size)
 {
-       struct libusb_bos_descriptor bos_header, *_bos;
-       struct libusb_bos_dev_capability_descriptor dev_cap;
-       int i;
+       struct libusb_bos_descriptor *_bos;
+       const struct usbi_bos_descriptor *bos_desc;
+       const struct usbi_descriptor_header *header;
+       uint8_t i;
 
        if (size < LIBUSB_DT_BOS_SIZE) {
                usbi_err(ctx, "short bos descriptor read %d/%d",
@@ -815,66 +772,61 @@ static int parse_bos(struct libusb_context *ctx,
                return LIBUSB_ERROR_IO;
        }
 
-       usbi_parse_descriptor(buffer, "bbwb", &bos_header, host_endian);
-       if (bos_header.bDescriptorType != LIBUSB_DT_BOS) {
-               usbi_err(ctx, "unexpected descriptor %x (expected %x)",
-                        bos_header.bDescriptorType, LIBUSB_DT_BOS);
+       bos_desc = (const struct usbi_bos_descriptor *)buffer;
+       if (bos_desc->bDescriptorType != LIBUSB_DT_BOS) {
+               usbi_err(ctx, "unexpected descriptor 0x%x (expected 0x%x)",
+                        bos_desc->bDescriptorType, LIBUSB_DT_BOS);
                return LIBUSB_ERROR_IO;
-       }
-       if (bos_header.bLength < LIBUSB_DT_BOS_SIZE) {
-               usbi_err(ctx, "invalid bos bLength (%d)", bos_header.bLength);
+       } else if (bos_desc->bLength < LIBUSB_DT_BOS_SIZE) {
+               usbi_err(ctx, "invalid bos bLength (%u)", bos_desc->bLength);
                return LIBUSB_ERROR_IO;
-       }
-       if (bos_header.bLength > size) {
-               usbi_err(ctx, "short bos descriptor read %d/%d",
-                        size, bos_header.bLength);
+       } else if (bos_desc->bLength > size) {
+               usbi_err(ctx, "short bos descriptor read %d/%u",
+                        size, bos_desc->bLength);
                return LIBUSB_ERROR_IO;
        }
 
-       _bos = calloc (1,
-               sizeof(*_bos) + bos_header.bNumDeviceCaps * sizeof(void *));
+       _bos = calloc(1, sizeof(*_bos) + bos_desc->bNumDeviceCaps * sizeof(void *));
        if (!_bos)
                return LIBUSB_ERROR_NO_MEM;
 
-       usbi_parse_descriptor(buffer, "bbwb", _bos, host_endian);
-       buffer += bos_header.bLength;
-       size -= bos_header.bLength;
+       parse_descriptor(buffer, "bbwb", _bos);
+       buffer += _bos->bLength;
+       size -= _bos->bLength;
 
        /* Get the device capability descriptors */
-       for (i = 0; i < bos_header.bNumDeviceCaps; i++) {
+       for (i = 0; i < _bos->bNumDeviceCaps; i++) {
                if (size < LIBUSB_DT_DEVICE_CAPABILITY_SIZE) {
                        usbi_warn(ctx, "short dev-cap descriptor read %d/%d",
                                  size, LIBUSB_DT_DEVICE_CAPABILITY_SIZE);
                        break;
                }
-               usbi_parse_descriptor(buffer, "bbb", &dev_cap, host_endian);
-               if (dev_cap.bDescriptorType != LIBUSB_DT_DEVICE_CAPABILITY) {
-                       usbi_warn(ctx, "unexpected descriptor %x (expected %x)",
-                                 dev_cap.bDescriptorType, LIBUSB_DT_DEVICE_CAPABILITY);
+               header = (const struct usbi_descriptor_header *)buffer;
+               if (header->bDescriptorType != LIBUSB_DT_DEVICE_CAPABILITY) {
+                       usbi_warn(ctx, "unexpected descriptor 0x%x (expected 0x%x)",
+                                 header->bDescriptorType, LIBUSB_DT_DEVICE_CAPABILITY);
                        break;
-               }
-               if (dev_cap.bLength < LIBUSB_DT_DEVICE_CAPABILITY_SIZE) {
-                       usbi_err(ctx, "invalid dev-cap bLength (%d)",
-                                dev_cap.bLength);
+               } else if (header->bLength < LIBUSB_DT_DEVICE_CAPABILITY_SIZE) {
+                       usbi_err(ctx, "invalid dev-cap bLength (%u)",
+                                header->bLength);
                        libusb_free_bos_descriptor(_bos);
                        return LIBUSB_ERROR_IO;
-               }
-               if (dev_cap.bLength > size) {
-                       usbi_warn(ctx, "short dev-cap descriptor read %d/%d",
-                                 size, dev_cap.bLength);
+               } else if (header->bLength > size) {
+                       usbi_warn(ctx, "short dev-cap descriptor read %d/%u",
+                                 size, header->bLength);
                        break;
                }
 
-               _bos->dev_capability[i] = malloc(dev_cap.bLength);
+               _bos->dev_capability[i] = malloc(header->bLength);
                if (!_bos->dev_capability[i]) {
                        libusb_free_bos_descriptor(_bos);
                        return LIBUSB_ERROR_NO_MEM;
                }
-               memcpy(_bos->dev_capability[i], buffer, dev_cap.bLength);
-               buffer += dev_cap.bLength;
-               size -= dev_cap.bLength;
+               memcpy(_bos->dev_capability[i], buffer, header->bLength);
+               buffer += header->bLength;
+               size -= header->bLength;
        }
-       _bos->bNumDeviceCaps = (uint8_t)i;
+       _bos->bNumDeviceCaps = i;
        *bos = _bos;
 
        return LIBUSB_SUCCESS;
@@ -894,16 +846,14 @@ static int parse_bos(struct libusb_context *ctx,
 int API_EXPORTED libusb_get_bos_descriptor(libusb_device_handle *dev_handle,
        struct libusb_bos_descriptor **bos)
 {
-       struct libusb_bos_descriptor _bos;
-       uint8_t bos_header[LIBUSB_DT_BOS_SIZE] = {0};
-       unsigned char *bos_data = NULL;
-       const int host_endian = 0;
+       union usbi_bos_desc_buf _bos;
+       uint16_t bos_len;
+       uint8_t *bos_data;
        int r;
 
        /* Read the BOS. This generates 2 requests on the bus,
         * one for the header, and one for the full BOS */
-       r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, bos_header,
-                                 LIBUSB_DT_BOS_SIZE);
+       r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, _bos.buf, sizeof(_bos.buf));
        if (r < 0) {
                if (r != LIBUSB_ERROR_PIPE)
                        usbi_err(HANDLE_CTX(dev_handle), "failed to read BOS (%d)", r);
@@ -915,19 +865,22 @@ int API_EXPORTED libusb_get_bos_descriptor(libusb_device_handle *dev_handle,
                return LIBUSB_ERROR_IO;
        }
 
-       usbi_parse_descriptor(bos_header, "bbwb", &_bos, host_endian);
-       usbi_dbg("found BOS descriptor: size %d bytes, %d capabilities",
-                _bos.wTotalLength, _bos.bNumDeviceCaps);
-       bos_data = calloc(_bos.wTotalLength, 1);
-       if (bos_data == NULL)
+       bos_len = libusb_le16_to_cpu(_bos.desc.wTotalLength);
+       usbi_dbg("found BOS descriptor: size %u bytes, %u capabilities",
+                bos_len, _bos.desc.bNumDeviceCaps);
+       bos_data = calloc(1, bos_len);
+       if (!bos_data)
                return LIBUSB_ERROR_NO_MEM;
 
-       r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, bos_data,
-                                 _bos.wTotalLength);
-       if (r >= 0)
-               r = parse_bos(HANDLE_CTX(dev_handle), bos, bos_data, r, host_endian);
-       else
+       r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, bos_data, bos_len);
+       if (r >= 0) {
+               if (r != (int)bos_len)
+                       usbi_warn(HANDLE_CTX(dev_handle), "short BOS read %d/%u",
+                                 r, bos_len);
+               r = parse_bos(HANDLE_CTX(dev_handle), bos, bos_data, r);
+       } else {
                usbi_err(HANDLE_CTX(dev_handle), "failed to read BOS (%d)", r);
+       }
 
        free(bos_data);
        return r;
@@ -942,7 +895,7 @@ int API_EXPORTED libusb_get_bos_descriptor(libusb_device_handle *dev_handle,
  */
 void API_EXPORTED libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos)
 {
-       int i;
+       uint8_t i;
 
        if (!bos)
                return;
@@ -966,21 +919,19 @@ void API_EXPORTED libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos)
  * \returns a LIBUSB_ERROR code on error
  */
 int API_EXPORTED libusb_get_usb_2_0_extension_descriptor(
-       struct libusb_context *ctx,
+       libusb_context *ctx,
        struct libusb_bos_dev_capability_descriptor *dev_cap,
        struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension)
 {
        struct libusb_usb_2_0_extension_descriptor *_usb_2_0_extension;
-       const int host_endian = 0;
 
        if (dev_cap->bDevCapabilityType != LIBUSB_BT_USB_2_0_EXTENSION) {
-               usbi_err(ctx, "unexpected bDevCapabilityType %x (expected %x)",
+               usbi_err(ctx, "unexpected bDevCapabilityType 0x%x (expected 0x%x)",
                         dev_cap->bDevCapabilityType,
                         LIBUSB_BT_USB_2_0_EXTENSION);
                return LIBUSB_ERROR_INVALID_PARAM;
-       }
-       if (dev_cap->bLength < LIBUSB_BT_USB_2_0_EXTENSION_SIZE) {
-               usbi_err(ctx, "short dev-cap descriptor read %d/%d",
+       } else if (dev_cap->bLength < LIBUSB_BT_USB_2_0_EXTENSION_SIZE) {
+               usbi_err(ctx, "short dev-cap descriptor read %u/%d",
                         dev_cap->bLength, LIBUSB_BT_USB_2_0_EXTENSION_SIZE);
                return LIBUSB_ERROR_IO;
        }
@@ -989,8 +940,7 @@ int API_EXPORTED libusb_get_usb_2_0_extension_descriptor(
        if (!_usb_2_0_extension)
                return LIBUSB_ERROR_NO_MEM;
 
-       usbi_parse_descriptor((unsigned char *)dev_cap, "bbbd",
-                             _usb_2_0_extension, host_endian);
+       parse_descriptor(dev_cap, "bbbd", _usb_2_0_extension);
 
        *usb_2_0_extension = _usb_2_0_extension;
        return LIBUSB_SUCCESS;
@@ -1024,21 +974,19 @@ void API_EXPORTED libusb_free_usb_2_0_extension_descriptor(
  * \returns a LIBUSB_ERROR code on error
  */
 int API_EXPORTED libusb_get_ss_usb_device_capability_descriptor(
-       struct libusb_context *ctx,
+       libusb_context *ctx,
        struct libusb_bos_dev_capability_descriptor *dev_cap,
        struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_cap)
 {
        struct libusb_ss_usb_device_capability_descriptor *_ss_usb_device_cap;
-       const int host_endian = 0;
 
        if (dev_cap->bDevCapabilityType != LIBUSB_BT_SS_USB_DEVICE_CAPABILITY) {
-               usbi_err(ctx, "unexpected bDevCapabilityType %x (expected %x)",
+               usbi_err(ctx, "unexpected bDevCapabilityType 0x%x (expected 0x%x)",
                         dev_cap->bDevCapabilityType,
                         LIBUSB_BT_SS_USB_DEVICE_CAPABILITY);
                return LIBUSB_ERROR_INVALID_PARAM;
-       }
-       if (dev_cap->bLength < LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) {
-               usbi_err(ctx, "short dev-cap descriptor read %d/%d",
+       } else if (dev_cap->bLength < LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) {
+               usbi_err(ctx, "short dev-cap descriptor read %u/%d",
                         dev_cap->bLength, LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE);
                return LIBUSB_ERROR_IO;
        }
@@ -1047,8 +995,7 @@ int API_EXPORTED libusb_get_ss_usb_device_capability_descriptor(
        if (!_ss_usb_device_cap)
                return LIBUSB_ERROR_NO_MEM;
 
-       usbi_parse_descriptor((unsigned char *)dev_cap, "bbbbwbbw",
-                             _ss_usb_device_cap, host_endian);
+       parse_descriptor(dev_cap, "bbbbwbbw", _ss_usb_device_cap);
 
        *ss_usb_device_cap = _ss_usb_device_cap;
        return LIBUSB_SUCCESS;
@@ -1060,7 +1007,8 @@ int API_EXPORTED libusb_get_ss_usb_device_capability_descriptor(
  * It is safe to call this function with a NULL ss_usb_device_cap
  * parameter, in which case the function simply returns.
  *
- * \param ss_usb_device_cap the USB 2.0 Extension descriptor to free
+ * \param ss_usb_device_cap the SuperSpeed USB Device Capability descriptor
+ * to free
  */
 void API_EXPORTED libusb_free_ss_usb_device_capability_descriptor(
        struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap)
@@ -1081,21 +1029,19 @@ void API_EXPORTED libusb_free_ss_usb_device_capability_descriptor(
  * \returns 0 on success
  * \returns a LIBUSB_ERROR code on error
  */
-int API_EXPORTED libusb_get_container_id_descriptor(struct libusb_context *ctx,
+int API_EXPORTED libusb_get_container_id_descriptor(libusb_context *ctx,
        struct libusb_bos_dev_capability_descriptor *dev_cap,
        struct libusb_container_id_descriptor **container_id)
 {
        struct libusb_container_id_descriptor *_container_id;
-       const int host_endian = 0;
 
        if (dev_cap->bDevCapabilityType != LIBUSB_BT_CONTAINER_ID) {
-               usbi_err(ctx, "unexpected bDevCapabilityType %x (expected %x)",
+               usbi_err(ctx, "unexpected bDevCapabilityType 0x%x (expected 0x%x)",
                         dev_cap->bDevCapabilityType,
                         LIBUSB_BT_CONTAINER_ID);
                return LIBUSB_ERROR_INVALID_PARAM;
-       }
-       if (dev_cap->bLength < LIBUSB_BT_CONTAINER_ID_SIZE) {
-               usbi_err(ctx, "short dev-cap descriptor read %d/%d",
+       } else if (dev_cap->bLength < LIBUSB_BT_CONTAINER_ID_SIZE) {
+               usbi_err(ctx, "short dev-cap descriptor read %u/%d",
                         dev_cap->bLength, LIBUSB_BT_CONTAINER_ID_SIZE);
                return LIBUSB_ERROR_IO;
        }
@@ -1104,8 +1050,7 @@ int API_EXPORTED libusb_get_container_id_descriptor(struct libusb_context *ctx,
        if (!_container_id)
                return LIBUSB_ERROR_NO_MEM;
 
-       usbi_parse_descriptor((unsigned char *)dev_cap, "bbbbu",
-                             _container_id, host_endian);
+       parse_descriptor(dev_cap, "bbbbu", _container_id);
 
        *container_id = _container_id;
        return LIBUSB_SUCCESS;
@@ -1117,7 +1062,7 @@ int API_EXPORTED libusb_get_container_id_descriptor(struct libusb_context *ctx,
  * It is safe to call this function with a NULL container_id parameter,
  * in which case the function simply returns.
  *
- * \param container_id the USB 2.0 Extension descriptor to free
+ * \param container_id the Container ID descriptor to free
  */
 void API_EXPORTED libusb_free_container_id_descriptor(
        struct libusb_container_id_descriptor *container_id)
@@ -1140,9 +1085,9 @@ void API_EXPORTED libusb_free_container_id_descriptor(
 int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev_handle,
        uint8_t desc_index, unsigned char *data, int length)
 {
-       unsigned char tbuf[255]; /* Some devices choke on size > 255 */
+       union usbi_string_desc_buf str;
        int r, si, di;
-       uint16_t langid;
+       uint16_t langid, wdata;
 
        /* Asking for the zero'th index is special - it returns a string
         * descriptor that contains all the language IDs supported by the
@@ -1156,35 +1101,37 @@ int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev_ha
        if (desc_index == 0)
                return LIBUSB_ERROR_INVALID_PARAM;
 
-       r = libusb_get_string_descriptor(dev_handle, 0, 0, tbuf, sizeof(tbuf));
+       r = libusb_get_string_descriptor(dev_handle, 0, 0, str.buf, 4);
        if (r < 0)
                return r;
-
-       if (r < 4)
+       else if (r != 4 || str.desc.bLength < 4)
                return LIBUSB_ERROR_IO;
+       else if (str.desc.bDescriptorType != LIBUSB_DT_STRING)
+               return LIBUSB_ERROR_IO;
+       else if (str.desc.bLength & 1)
+               usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for string descriptor", str.desc.bLength);
 
-       langid = tbuf[2] | (tbuf[3] << 8);
-
-       r = libusb_get_string_descriptor(dev_handle, desc_index, langid, tbuf,
-               sizeof(tbuf));
+       langid = libusb_le16_to_cpu(str.desc.wData[0]);
+       r = libusb_get_string_descriptor(dev_handle, desc_index, langid, str.buf, sizeof(str.buf));
        if (r < 0)
                return r;
-
-       if (tbuf[1] != LIBUSB_DT_STRING)
+       else if (r < DESC_HEADER_LENGTH || str.desc.bLength > r)
                return LIBUSB_ERROR_IO;
-
-       if (tbuf[0] > r)
+       else if (str.desc.bDescriptorType != LIBUSB_DT_STRING)
                return LIBUSB_ERROR_IO;
+       else if ((str.desc.bLength & 1) || str.desc.bLength != r)
+               usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for string descriptor", str.desc.bLength);
 
        di = 0;
-       for (si = 2; si < tbuf[0]; si += 2) {
+       for (si = 2; si < str.desc.bLength; si += 2) {
                if (di >= (length - 1))
                        break;
 
-               if ((tbuf[si] & 0x80) || (tbuf[si + 1])) /* non-ASCII */
-                       data[di++] = '?';
+               wdata = libusb_le16_to_cpu(str.desc.wData[di]);
+               if (wdata < 0x80)
+                       data[di++] = (unsigned char)wdata;
                else
-                       data[di++] = tbuf[si];
+                       data[di++] = '?'; /* non-ASCII */
        }
 
        data[di] = 0;
index a4320bc42e593917c65a9d83af11dfc1e0708512..e3e5e76e688d5772e5aca480a602bcd3401cf4a9 100644 (file)
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#include <config.h>
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-#include <assert.h>
-
 #include "libusbi.h"
 #include "hotplug.h"
 
@@ -48,7 +37,7 @@
  * Version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102, has added support
  * for hotplug events on <b>some</b> platforms (you should test if your platform
  * supports hotplug notification by calling \ref libusb_has_capability() with
- * parameter \ref LIBUSB_CAP_HAS_HOTPLUG). 
+ * parameter \ref LIBUSB_CAP_HAS_HOTPLUG).
  *
  * This interface allows you to request notification for the arrival and departure
  * of matching USB devices.
@@ -61,8 +50,8 @@
  * expecting additional events. Returning 0 will rearm the callback and 1 will cause
  * the callback to be deregistered. Note that when callbacks are called from
  * libusb_hotplug_register_callback() because of the \ref LIBUSB_HOTPLUG_ENUMERATE
- * flag, the callback return value is ignored, iow you cannot cause a callback
- * to be deregistered by returning 1 when it is called from
+ * flag, the callback return value is ignored. In other words, you cannot cause a
+ * callback to be deregistered by returning 1 when it is called from
  * libusb_hotplug_register_callback().
  *
  * Callbacks for a particular context are automatically deregistered by libusb_exit().
@@ -154,6 +143,13 @@ int main (void) {
 \endcode
  */
 
+#define VALID_HOTPLUG_EVENTS                   \
+        (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | \
+         LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
+
+#define VALID_HOTPLUG_FLAGS                    \
+        (LIBUSB_HOTPLUG_ENUMERATE)
+
 static int usbi_hotplug_match_cb(struct libusb_context *ctx,
        struct libusb_device *dev, libusb_hotplug_event event,
        struct libusb_hotplug_callback *hotplug_cb)
@@ -188,7 +184,7 @@ void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
 
        usbi_mutex_lock(&ctx->hotplug_cbs_lock);
 
-       list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) {
+       for_each_hotplug_cb_safe(ctx, hotplug_cb, next) {
                if (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE) {
                        /* process deregistration in usbi_hotplug_deregister() */
                        continue;
@@ -210,8 +206,8 @@ void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
 void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,
        libusb_hotplug_event event)
 {
-       int pending_events;
        struct libusb_hotplug_message *message = calloc(1, sizeof(*message));
+       unsigned int event_flags;
 
        if (!message) {
                usbi_err(ctx, "error allocating hotplug message");
@@ -224,15 +220,16 @@ void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device
        /* Take the event data lock and add this message to the list.
         * Only signal an event if there are no prior pending events. */
        usbi_mutex_lock(&ctx->event_data_lock);
-       pending_events = usbi_pending_events(ctx);
+       event_flags = ctx->event_flags;
+       ctx->event_flags |= USBI_EVENT_HOTPLUG_MSG_PENDING;
        list_add_tail(&message->list, &ctx->hotplug_msgs);
-       if (!pending_events)
-               usbi_signal_event(ctx);
+       if (!event_flags)
+               usbi_signal_event(&ctx->event);
        usbi_mutex_unlock(&ctx->event_data_lock);
 }
 
 int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
-       libusb_hotplug_event events, libusb_hotplug_flag flags,
+       int events, int flags,
        int vendor_id, int product_id, int dev_class,
        libusb_hotplug_callback_fn cb_fn, void *user_data,
        libusb_hotplug_callback_handle *callback_handle)
@@ -240,8 +237,8 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
        struct libusb_hotplug_callback *new_callback;
 
        /* check for sane values */
-       if ((!events || (~(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) & events)) ||
-           (flags && (~LIBUSB_HOTPLUG_ENUMERATE & flags)) ||
+       if ((!events || (~VALID_HOTPLUG_EVENTS & events)) ||
+           (~VALID_HOTPLUG_FLAGS & flags) ||
            (LIBUSB_HOTPLUG_MATCH_ANY != vendor_id && (~0xffff & vendor_id)) ||
            (LIBUSB_HOTPLUG_MATCH_ANY != product_id && (~0xffff & product_id)) ||
            (LIBUSB_HOTPLUG_MATCH_ANY != dev_class && (~0xff & dev_class)) ||
@@ -254,7 +251,7 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
                return LIBUSB_ERROR_NOT_SUPPORTED;
        }
 
-       USBI_GET_CONTEXT(ctx);
+       ctx = usbi_get_context(ctx);
 
        new_callback = calloc(1, sizeof(*new_callback));
        if (!new_callback) {
@@ -319,7 +316,7 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
        return LIBUSB_SUCCESS;
 }
 
-void API_EXPORTED libusb_hotplug_deregister_callback(struct libusb_context *ctx,
+void API_EXPORTED libusb_hotplug_deregister_callback(libusb_context *ctx,
        libusb_hotplug_callback_handle callback_handle)
 {
        struct libusb_hotplug_callback *hotplug_cb;
@@ -330,12 +327,12 @@ void API_EXPORTED libusb_hotplug_deregister_callback(struct libusb_context *ctx,
                return;
        }
 
-       USBI_GET_CONTEXT(ctx);
-
        usbi_dbg("deregister hotplug cb %d", callback_handle);
 
+       ctx = usbi_get_context(ctx);
+
        usbi_mutex_lock(&ctx->hotplug_cbs_lock);
-       list_for_each_entry(hotplug_cb, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) {
+       for_each_hotplug_cb(ctx, hotplug_cb) {
                if (callback_handle == hotplug_cb->handle) {
                        /* Mark this callback for deregistration */
                        hotplug_cb->flags |= USBI_HOTPLUG_NEEDS_FREE;
@@ -345,23 +342,50 @@ void API_EXPORTED libusb_hotplug_deregister_callback(struct libusb_context *ctx,
        usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
 
        if (deregistered) {
-               int pending_events;
+               unsigned int event_flags;
 
                usbi_mutex_lock(&ctx->event_data_lock);
-               pending_events = usbi_pending_events(ctx);
+               event_flags = ctx->event_flags;
                ctx->event_flags |= USBI_EVENT_HOTPLUG_CB_DEREGISTERED;
-               if (!pending_events)
-                       usbi_signal_event(ctx);
+               if (!event_flags)
+                       usbi_signal_event(&ctx->event);
                usbi_mutex_unlock(&ctx->event_data_lock);
        }
 }
 
+DEFAULT_VISIBILITY
+void * LIBUSB_CALL libusb_hotplug_get_user_data(libusb_context *ctx,
+       libusb_hotplug_callback_handle callback_handle)
+{
+       struct libusb_hotplug_callback *hotplug_cb;
+       void *user_data = NULL;
+
+       /* check for hotplug support */
+       if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+               return NULL;
+       }
+
+       usbi_dbg("get hotplug user data %d", callback_handle);
+
+       ctx = usbi_get_context(ctx);
+
+       usbi_mutex_lock(&ctx->hotplug_cbs_lock);
+       for_each_hotplug_cb(ctx, hotplug_cb) {
+               if (callback_handle == hotplug_cb->handle) {
+                       user_data = hotplug_cb->user_data;
+               }
+       }
+       usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
+
+       return user_data;
+}
+
 void usbi_hotplug_deregister(struct libusb_context *ctx, int forced)
 {
        struct libusb_hotplug_callback *hotplug_cb, *next;
 
        usbi_mutex_lock(&ctx->hotplug_cbs_lock);
-       list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) {
+       for_each_hotplug_cb_safe(ctx, hotplug_cb, next) {
                if (forced || (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE)) {
                        usbi_dbg("freeing hotplug cb %p with handle %d", hotplug_cb,
                                 hotplug_cb->handle);
index dbadbcb93d54a766ffe685b94775dd0f2458bbf5..161f7e5fcde20676a9b90d6d31d55e0ae561ec40 100644 (file)
@@ -36,16 +36,16 @@ enum usbi_hotplug_flags {
         */
 
        /* The vendor_id field is valid for matching */
-       USBI_HOTPLUG_VENDOR_ID_VALID = (1 << 3),
+       USBI_HOTPLUG_VENDOR_ID_VALID = (1U << 3),
 
        /* The product_id field is valid for matching */
-       USBI_HOTPLUG_PRODUCT_ID_VALID = (1 << 4),
+       USBI_HOTPLUG_PRODUCT_ID_VALID = (1U << 4),
 
        /* The dev_class field is valid for matching */
-       USBI_HOTPLUG_DEV_CLASS_VALID = (1 << 5),
+       USBI_HOTPLUG_DEV_CLASS_VALID = (1U << 5),
 
        /* This callback has been unregistered and needs to be freed */
-       USBI_HOTPLUG_NEEDS_FREE = (1 << 6),
+       USBI_HOTPLUG_NEEDS_FREE = (1U << 6),
 };
 
 /** \ingroup hotplug
@@ -90,10 +90,16 @@ struct libusb_hotplug_message {
        struct list_head list;
 };
 
+#define for_each_hotplug_cb(ctx, c) \
+       for_each_helper(c, &(ctx)->hotplug_cbs, struct libusb_hotplug_callback)
+
+#define for_each_hotplug_cb_safe(ctx, c, n) \
+       for_each_safe_helper(c, n, &(ctx)->hotplug_cbs, struct libusb_hotplug_callback)
+
 void usbi_hotplug_deregister(struct libusb_context *ctx, int forced);
 void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
-                       libusb_hotplug_event event);
+       libusb_hotplug_event event);
 void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,
-                       libusb_hotplug_event event);
+       libusb_hotplug_event event);
 
 #endif
index a03bfaae1a9e21b352f79c72e4b92ee4a6b71e5d..0e960ddfddd5292db40d8829084079eb081ad69d 100644 (file)
@@ -3,6 +3,8 @@
  * I/O functions for libusb
  * Copyright © 2007-2009 Daniel Drake <dsd@gentoo.org>
  * Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com>
+ * Copyright © 2019 Nathan Hjelm <hjelmn@cs.umm.edu>
+ * Copyright © 2019 Google LLC. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#include <config.h>
-
-#include <assert.h>
-#include <errno.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-#ifdef USBI_TIMERFD_AVAILABLE
-#include <sys/timerfd.h>
-#endif
-
 #include "libusbi.h"
 #include "hotplug.h"
 
@@ -86,7 +73,7 @@
  * a single function call. When the function call returns, the transfer has
  * completed and you can parse the results.
  *
- * If you have used the libusb-0.1 before, this I/O style will seem familar to
+ * If you have used the libusb-0.1 before, this I/O style will seem familiar to
  * you. libusb-0.1 only offered a synchronous interface.
  *
  * In our input device example, to read button presses you might write code
@@ -112,7 +99,7 @@ if (r == 0 && actual_length == sizeof(data)) {
  * sleeping for that long. Execution will be tied up inside the library -
  * the entire thread will be useless for that duration.
  *
- * Another issue is that by tieing up the thread with that single transaction
+ * Another issue is that by tying up the thread with that single transaction
  * there is no possibility of performing I/O with multiple endpoints and/or
  * multiple devices simultaneously, unless you resort to creating one thread
  * per transaction.
@@ -330,9 +317,40 @@ if (r == 0 && actual_length == sizeof(data)) {
  * Freeing the transfer after it has been cancelled but before cancellation
  * has completed will result in undefined behaviour.
  *
+ * \attention
  * When a transfer is cancelled, some of the data may have been transferred.
- * libusb will communicate this to you in the transfer callback. Do not assume
- * that no data was transferred.
+ * libusb will communicate this to you in the transfer callback.
+ * <b>Do not assume that no data was transferred.</b>
+ *
+ * \section asyncpartial Partial data transfer resulting from cancellation
+ *
+ * As noted above, some of the data may have been transferred at the time a
+ * transfer is cancelled. It is helpful to see how this is possible if you
+ * consider a bulk transfer to an endpoint with a packet size of 64 bytes.
+ * Supposing you submit a 512-byte transfer to this endpoint, the operating
+ * system will divide this transfer up into 8 separate 64-byte frames that the
+ * host controller will schedule for the device to transfer data. If this
+ * transfer is cancelled while the device is transferring data, a subset of
+ * these frames may be descheduled from the host controller before the device
+ * has the opportunity to finish transferring data to the host.
+ *
+ * What your application should do with a partial data transfer is a policy
+ * decision; there is no single answer that satisfies the needs of every
+ * application. The data that was successfully transferred should be
+ * considered entirely valid, but your application must decide what to do with
+ * the remaining data that was not transferred. Some possible actions to take
+ * are:
+ * - Resubmit another transfer for the remaining data, possibly with a shorter
+ *   timeout
+ * - Discard the partially transferred data and report an error
+ *
+ * \section asynctimeout Timeouts
+ *
+ * When a transfer times out, libusb internally notes this and attempts to
+ * cancel the transfer. As noted in \ref asyncpartial "above", it is possible
+ * that some of the data may actually have been transferred. Your application
+ * should <b>always</b> check how much data was actually transferred once the
+ * transfer completes and act accordingly.
  *
  * \section bulk_overflows Overflows on device-to-host bulk/interrupt endpoints
  *
@@ -390,7 +408,7 @@ if (r == 0 && actual_length == sizeof(data)) {
  * wLength of the setup packet, rather than the size of the data buffer. So,
  * if your wLength was 4, your transfer's <tt>length</tt> was 12, then you
  * should expect an <tt>actual_length</tt> of 4 to indicate that the data was
- * transferred in entirity.
+ * transferred in entirety.
  *
  * To simplify parsing of setup packets and obtaining the data from the
  * correct offset, you may wish to use the libusb_control_transfer_get_data()
@@ -469,14 +487,21 @@ if (r == 0 && actual_length == sizeof(data)) {
  * libusb_get_iso_packet_buffer() and libusb_get_iso_packet_buffer_simple()
  * functions may help you here.
  *
- * <b>Note</b>: Some operating systems (e.g. Linux) may impose limits on the
- * length of individual isochronous packets and/or the total length of the
- * isochronous transfer. Such limits can be difficult for libusb to detect,
- * so the library will simply try and submit the transfer as set up by you.
- * If the transfer fails to submit because it is too large,
+ * \section asynclimits Transfer length limitations
+ *
+ * Some operating systems may impose limits on the length of the transfer data
+ * buffer or, in the case of isochronous transfers, the length of individual
+ * isochronous packets. Such limits can be difficult for libusb to detect, so
+ * in most cases the library will simply try and submit the transfer as set up
+ * by you. If the transfer fails to submit because it is too large,
  * libusb_submit_transfer() will return
  * \ref libusb_error::LIBUSB_ERROR_INVALID_PARAM "LIBUSB_ERROR_INVALID_PARAM".
  *
+ * The following are known limits for control transfer lengths. Note that this
+ * length includes the 8-byte setup packet.
+ * - Linux (4,096 bytes)
+ * - Windows (4,096 bytes)
+ *
  * \section asyncmem Memory caveats
  *
  * In most circumstances, it is not safe to use stack memory for transfer
@@ -517,7 +542,14 @@ if (r == 0 && actual_length == sizeof(data)) {
  * application must call into when libusb has work do to. This gives libusb
  * the opportunity to reap pending transfers, invoke callbacks, etc.
  *
- * There are 2 different approaches to dealing with libusb_handle_events:
+ * \note
+ * All event handling is performed by whichever thread calls the
+ * libusb_handle_events() function. libusb does not invoke any callbacks
+ * outside of this context. Consequently, any callbacks will be run on the
+ * thread that calls the libusb_handle_events() function.
+ *
+ * When to call the libusb_handle_events() function depends on which model
+ * your application decides to use. The 2 different approaches:
  *
  * -# Repeatedly call libusb_handle_events() in blocking mode from a dedicated
  *    thread.
@@ -538,7 +570,7 @@ if (r == 0 && actual_length == sizeof(data)) {
  *
  * Lets begin with stating the obvious: If you're going to use a separate
  * thread for libusb event handling, your callback functions MUST be
- * threadsafe.
+ * thread-safe.
  *
  * Other then that doing event handling from a separate thread, is mostly
  * simple. You can use an event thread function as follows:
@@ -778,7 +810,7 @@ while (user has not requested application exit) {
  * system calls. This is directly exposed at the
  * \ref libusb_asyncio "asynchronous interface" but it is important to note that the
  * \ref libusb_syncio "synchronous interface" is implemented on top of the
- * asynchonrous interface, therefore the same considerations apply.
+ * asynchronous interface, therefore the same considerations apply.
  *
  * The issue is that if two or more threads are concurrently calling poll()
  * or select() on libusb's file descriptors then only one of those threads
@@ -886,6 +918,11 @@ void myfunc() {
  * do is submit a single transfer and wait for its completion, then using
  * one of the synchronous I/O functions is much easier.
  *
+ * \note
+ * The `completed` variable must be modified while holding the event lock,
+ * otherwise a race condition can still exist. It is simplest to do so from
+ * within the transfer callback as shown above.
+ *
  * \section eventlock The events lock
  *
  * The problem is when we consider the fact that libusb exposes file
@@ -983,7 +1020,7 @@ printf("completed!\n");
  * event handling), because the event waiter seems to have taken the event
  * waiters lock while waiting for an event. However, the system does support
  * multiple event waiters, because libusb_wait_for_event() actually drops
- * the lock while waiting, and reaquires it before continuing.
+ * the lock while waiting, and reacquires it before continuing.
  *
  * We have now implemented code which can dynamically handle situations where
  * nobody is handling events (so we should do it ourselves), and it can also
@@ -1128,45 +1165,40 @@ int usbi_io_init(struct libusb_context *ctx)
        usbi_mutex_init(&ctx->event_data_lock);
        usbi_tls_key_create(&ctx->event_handling_key);
        list_init(&ctx->flying_transfers);
-       list_init(&ctx->ipollfds);
+       list_init(&ctx->event_sources);
+       list_init(&ctx->removed_event_sources);
        list_init(&ctx->hotplug_msgs);
        list_init(&ctx->completed_transfers);
 
-       /* FIXME should use an eventfd on kernels that support it */
-       r = usbi_pipe(ctx->event_pipe);
-       if (r < 0) {
-               r = LIBUSB_ERROR_OTHER;
+       r = usbi_create_event(&ctx->event);
+       if (r < 0)
                goto err;
-       }
 
-       r = usbi_add_pollfd(ctx, ctx->event_pipe[0], POLLIN);
+       r = usbi_add_event_source(ctx, USBI_EVENT_OS_HANDLE(&ctx->event), USBI_EVENT_POLL_EVENTS);
        if (r < 0)
-               goto err_close_pipe;
-
-#ifdef USBI_TIMERFD_AVAILABLE
-       ctx->timerfd = timerfd_create(usbi_backend.get_timerfd_clockid(),
-               TFD_NONBLOCK | TFD_CLOEXEC);
-       if (ctx->timerfd >= 0) {
-               usbi_dbg("using timerfd for timeouts");
-               r = usbi_add_pollfd(ctx, ctx->timerfd, POLLIN);
+               goto err_destroy_event;
+
+#ifdef HAVE_OS_TIMER
+       r = usbi_create_timer(&ctx->timer);
+       if (r == 0) {
+               usbi_dbg("using timer for timeouts");
+               r = usbi_add_event_source(ctx, USBI_TIMER_OS_HANDLE(&ctx->timer), USBI_TIMER_POLL_EVENTS);
                if (r < 0)
-                       goto err_close_timerfd;
+                       goto err_destroy_timer;
        } else {
-               usbi_dbg("timerfd not available (code %d error %d)", ctx->timerfd, errno);
-               ctx->timerfd = -1;
+               usbi_dbg("timer not available for timeouts");
        }
 #endif
 
        return 0;
 
-#ifdef USBI_TIMERFD_AVAILABLE
-err_close_timerfd:
-       close(ctx->timerfd);
-       usbi_remove_pollfd(ctx, ctx->event_pipe[0]);
+#ifdef HAVE_OS_TIMER
+err_destroy_timer:
+       usbi_destroy_timer(&ctx->timer);
+       usbi_remove_event_source(ctx, USBI_EVENT_OS_HANDLE(&ctx->event));
 #endif
-err_close_pipe:
-       usbi_close(ctx->event_pipe[0]);
-       usbi_close(ctx->event_pipe[1]);
+err_destroy_event:
+       usbi_destroy_event(&ctx->event);
 err:
        usbi_mutex_destroy(&ctx->flying_transfers_lock);
        usbi_mutex_destroy(&ctx->events_lock);
@@ -1177,56 +1209,54 @@ err:
        return r;
 }
 
+static void cleanup_removed_event_sources(struct libusb_context *ctx)
+{
+       struct usbi_event_source *ievent_source, *tmp;
+
+       for_each_removed_event_source_safe(ctx, ievent_source, tmp) {
+               list_del(&ievent_source->list);
+               free(ievent_source);
+       }
+}
+
 void usbi_io_exit(struct libusb_context *ctx)
 {
-       usbi_remove_pollfd(ctx, ctx->event_pipe[0]);
-       usbi_close(ctx->event_pipe[0]);
-       usbi_close(ctx->event_pipe[1]);
-#ifdef USBI_TIMERFD_AVAILABLE
-       if (usbi_using_timerfd(ctx)) {
-               usbi_remove_pollfd(ctx, ctx->timerfd);
-               close(ctx->timerfd);
+#ifdef HAVE_OS_TIMER
+       if (usbi_using_timer(ctx)) {
+               usbi_remove_event_source(ctx, USBI_TIMER_OS_HANDLE(&ctx->timer));
+               usbi_destroy_timer(&ctx->timer);
        }
 #endif
+       usbi_remove_event_source(ctx, USBI_EVENT_OS_HANDLE(&ctx->event));
+       usbi_destroy_event(&ctx->event);
        usbi_mutex_destroy(&ctx->flying_transfers_lock);
        usbi_mutex_destroy(&ctx->events_lock);
        usbi_mutex_destroy(&ctx->event_waiters_lock);
        usbi_cond_destroy(&ctx->event_waiters_cond);
        usbi_mutex_destroy(&ctx->event_data_lock);
        usbi_tls_key_delete(ctx->event_handling_key);
-       if (ctx->pollfds)
-               free(ctx->pollfds);
+       cleanup_removed_event_sources(ctx);
+       free(ctx->event_data);
 }
 
-static int calculate_timeout(struct usbi_transfer *transfer)
+static void calculate_timeout(struct usbi_transfer *itransfer)
 {
-       int r;
-       struct timespec current_time;
        unsigned int timeout =
-               USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout;
+               USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout;
 
        if (!timeout) {
-               timerclear(&transfer->timeout);
-               return 0;
-       }
-
-       r = usbi_backend.clock_gettime(USBI_CLOCK_MONOTONIC, &current_time);
-       if (r < 0) {
-               usbi_err(ITRANSFER_CTX(transfer),
-                       "failed to read monotonic clock, errno=%d", errno);
-               return r;
+               TIMESPEC_CLEAR(&itransfer->timeout);
+               return;
        }
 
-       current_time.tv_sec += timeout / 1000;
-       current_time.tv_nsec += (timeout % 1000) * 1000000;
+       usbi_get_monotonic_time(&itransfer->timeout);
 
-       while (current_time.tv_nsec >= 1000000000) {
-               current_time.tv_nsec -= 1000000000;
-               current_time.tv_sec++;
+       itransfer->timeout.tv_sec += timeout / 1000U;
+       itransfer->timeout.tv_nsec += (timeout % 1000U) * 1000000L;
+       if (itransfer->timeout.tv_nsec >= NSEC_PER_SEC) {
+               ++itransfer->timeout.tv_sec;
+               itransfer->timeout.tv_nsec -= NSEC_PER_SEC;
        }
-
-       TIMESPEC_TO_TIMEVAL(&transfer->timeout, &current_time);
-       return 0;
 }
 
 /** \ingroup libusb_asyncio
@@ -1249,24 +1279,35 @@ static int calculate_timeout(struct usbi_transfer *transfer)
  * use it on a non-isochronous endpoint. If you do this, ensure that at time
  * of submission, num_iso_packets is 0 and that type is set appropriately.
  *
- * \param iso_packets number of isochronous packet descriptors to allocate
+ * \param iso_packets number of isochronous packet descriptors to allocate. Must be non-negative.
  * \returns a newly allocated transfer, or NULL on error
  */
 DEFAULT_VISIBILITY
 struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(
        int iso_packets)
 {
+       size_t priv_size;
+       size_t alloc_size;
+       unsigned char *ptr;
+       struct usbi_transfer *itransfer;
        struct libusb_transfer *transfer;
-       size_t os_alloc_size = usbi_backend.transfer_priv_size;
-       size_t alloc_size = sizeof(struct usbi_transfer)
+
+       assert(iso_packets >= 0);
+       if (iso_packets < 0)
+               return NULL;
+
+       priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size);
+       alloc_size = priv_size
+               + sizeof(struct usbi_transfer)
                + sizeof(struct libusb_transfer)
-               + (sizeof(struct libusb_iso_packet_descriptor) * iso_packets)
-               + os_alloc_size;
-       struct usbi_transfer *itransfer = calloc(1, alloc_size);
-       if (!itransfer)
+               + (sizeof(struct libusb_iso_packet_descriptor) * (size_t)iso_packets);
+       ptr = calloc(1, alloc_size);
+       if (!ptr)
                return NULL;
 
+       itransfer = (struct usbi_transfer *)(ptr + priv_size);
        itransfer->num_iso_packets = iso_packets;
+       itransfer->priv = ptr;
        usbi_mutex_init(&itransfer->lock);
        transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
        usbi_dbg("transfer %p", transfer);
@@ -1293,67 +1334,58 @@ struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(
 void API_EXPORTED libusb_free_transfer(struct libusb_transfer *transfer)
 {
        struct usbi_transfer *itransfer;
+       size_t priv_size;
+       unsigned char *ptr;
+
        if (!transfer)
                return;
 
        usbi_dbg("transfer %p", transfer);
-       if (transfer->flags & LIBUSB_TRANSFER_FREE_BUFFER && transfer->buffer)
+       if (transfer->flags & LIBUSB_TRANSFER_FREE_BUFFER)
                free(transfer->buffer);
 
        itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
        usbi_mutex_destroy(&itransfer->lock);
-       free(itransfer);
-}
 
-#ifdef USBI_TIMERFD_AVAILABLE
-static int disarm_timerfd(struct libusb_context *ctx)
-{
-       const struct itimerspec disarm_timer = { { 0, 0 }, { 0, 0 } };
-       int r;
-
-       usbi_dbg("");
-       r = timerfd_settime(ctx->timerfd, 0, &disarm_timer, NULL);
-       if (r < 0)
-               return LIBUSB_ERROR_OTHER;
-       else
-               return 0;
+       priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size);
+       ptr = (unsigned char *)itransfer - priv_size;
+       assert(ptr == itransfer->priv);
+       free(ptr);
 }
 
-/* iterates through the flying transfers, and rearms the timerfd based on the
+/* iterates through the flying transfers, and rearms the timer based on the
  * next upcoming timeout.
  * must be called with flying_list locked.
  * returns 0 on success or a LIBUSB_ERROR code on failure.
  */
-static int arm_timerfd_for_next_timeout(struct libusb_context *ctx)
+#ifdef HAVE_OS_TIMER
+static int arm_timer_for_next_timeout(struct libusb_context *ctx)
 {
-       struct usbi_transfer *transfer;
+       struct usbi_transfer *itransfer;
+
+       if (!usbi_using_timer(ctx))
+               return 0;
 
-       list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) {
-               struct timeval *cur_tv = &transfer->timeout;
+       for_each_transfer(ctx, itransfer) {
+               struct timespec *cur_ts = &itransfer->timeout;
 
                /* if we've reached transfers of infinite timeout, then we have no
                 * arming to do */
-               if (!timerisset(cur_tv))
-                       goto disarm;
+               if (!TIMESPEC_IS_SET(cur_ts))
+                       break;
 
                /* act on first transfer that has not already been handled */
-               if (!(transfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))) {
-                       int r;
-                       const struct itimerspec it = { {0, 0},
-                               { cur_tv->tv_sec, cur_tv->tv_usec * 1000 } };
-                       usbi_dbg("next timeout originally %dms", USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout);
-                       r = timerfd_settime(ctx->timerfd, TFD_TIMER_ABSTIME, &it, NULL);
-                       if (r < 0)
-                               return LIBUSB_ERROR_OTHER;
-                       return 0;
+               if (!(itransfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))) {
+                       usbi_dbg("next timeout originally %ums", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout);
+                       return usbi_arm_timer(&ctx->timer, cur_ts);
                }
        }
 
-disarm:
-       return disarm_timerfd(ctx);
+       usbi_dbg("no timeouts, disarming timer");
+       return usbi_disarm_timer(&ctx->timer);
 }
 #else
-static int arm_timerfd_for_next_timeout(struct libusb_context *ctx)
+static inline int arm_timer_for_next_timeout(struct libusb_context *ctx)
 {
        UNUSED(ctx);
        return 0;
@@ -1363,40 +1395,36 @@ static int arm_timerfd_for_next_timeout(struct libusb_context *ctx)
 /* add a transfer to the (timeout-sorted) active transfers list.
  * This function will return non 0 if fails to update the timer,
  * in which case the transfer is *not* on the flying_transfers list. */
-static int add_to_flying_list(struct usbi_transfer *transfer)
+static int add_to_flying_list(struct usbi_transfer *itransfer)
 {
        struct usbi_transfer *cur;
-       struct timeval *timeout = &transfer->timeout;
-       struct libusb_context *ctx = ITRANSFER_CTX(transfer);
-       int r;
+       struct timespec *timeout = &itransfer->timeout;
+       struct libusb_context *ctx = ITRANSFER_CTX(itransfer);
+       int r = 0;
        int first = 1;
 
-       r = calculate_timeout(transfer);
-       if (r)
-               return r;
+       calculate_timeout(itransfer);
 
        /* if we have no other flying transfers, start the list with this one */
        if (list_empty(&ctx->flying_transfers)) {
-               list_add(&transfer->list, &ctx->flying_transfers);
+               list_add(&itransfer->list, &ctx->flying_transfers);
                goto out;
        }
 
        /* if we have infinite timeout, append to end of list */
-       if (!timerisset(timeout)) {
-               list_add_tail(&transfer->list, &ctx->flying_transfers);
+       if (!TIMESPEC_IS_SET(timeout)) {
+               list_add_tail(&itransfer->list, &ctx->flying_transfers);
                /* first is irrelevant in this case */
                goto out;
        }
 
        /* otherwise, find appropriate place in list */
-       list_for_each_entry(cur, &ctx->flying_transfers, list, struct usbi_transfer) {
+       for_each_transfer(ctx, cur) {
                /* find first timeout that occurs after the transfer in question */
-               struct timeval *cur_tv = &cur->timeout;
+               struct timespec *cur_ts = &cur->timeout;
 
-               if (!timerisset(cur_tv) || (cur_tv->tv_sec > timeout->tv_sec) ||
-                               (cur_tv->tv_sec == timeout->tv_sec &&
-                                       cur_tv->tv_usec > timeout->tv_usec)) {
-                       list_add_tail(&transfer->list, &cur->list);
+               if (!TIMESPEC_IS_SET(cur_ts) || TIMESPEC_CMP(cur_ts, timeout, >)) {
+                       list_add_tail(&itransfer->list, &cur->list);
                        goto out;
                }
                first = 0;
@@ -1404,28 +1432,22 @@ static int add_to_flying_list(struct usbi_transfer *transfer)
        /* first is 0 at this stage (list not empty) */
 
        /* otherwise we need to be inserted at the end */
-       list_add_tail(&transfer->list, &ctx->flying_transfers);
+       list_add_tail(&itransfer->list, &ctx->flying_transfers);
 out:
-#ifdef USBI_TIMERFD_AVAILABLE
-       if (first && usbi_using_timerfd(ctx) && timerisset(timeout)) {
+#ifdef HAVE_OS_TIMER
+       if (first && usbi_using_timer(ctx) && TIMESPEC_IS_SET(timeout)) {
                /* if this transfer has the lowest timeout of all active transfers,
-                * rearm the timerfd with this transfer's timeout */
-               const struct itimerspec it = { {0, 0},
-                       { timeout->tv_sec, timeout->tv_usec * 1000 } };
-               usbi_dbg("arm timerfd for timeout in %dms (first in line)",
-                       USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout);
-               r = timerfd_settime(ctx->timerfd, TFD_TIMER_ABSTIME, &it, NULL);
-               if (r < 0) {
-                       usbi_warn(ctx, "failed to arm first timerfd (errno %d)", errno);
-                       r = LIBUSB_ERROR_OTHER;
-               }
+                * rearm the timer with this transfer's timeout */
+               usbi_dbg("arm timer for timeout in %ums (first in line)",
+                       USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout);
+               r = usbi_arm_timer(&ctx->timer, timeout);
        }
 #else
        UNUSED(first);
 #endif
 
        if (r)
-               list_del(&transfer->list);
+               list_del(&itransfer->list);
 
        return r;
 }
@@ -1434,18 +1456,18 @@ out:
  * This function will *always* remove the transfer from the
  * flying_transfers list. It will return a LIBUSB_ERROR code
  * if it fails to update the timer for the next timeout. */
-static int remove_from_flying_list(struct usbi_transfer *transfer)
+static int remove_from_flying_list(struct usbi_transfer *itransfer)
 {
-       struct libusb_context *ctx = ITRANSFER_CTX(transfer);
-       int rearm_timerfd;
+       struct libusb_context *ctx = ITRANSFER_CTX(itransfer);
+       int rearm_timer;
        int r = 0;
 
        usbi_mutex_lock(&ctx->flying_transfers_lock);
-       rearm_timerfd = (timerisset(&transfer->timeout) &&
-               list_first_entry(&ctx->flying_transfers, struct usbi_transfer, list) == transfer);
-       list_del(&transfer->list);
-       if (usbi_using_timerfd(ctx) && rearm_timerfd)
-               r = arm_timerfd_for_next_timeout(ctx);
+       rearm_timer = (TIMESPEC_IS_SET(&itransfer->timeout) &&
+               list_first_entry(&ctx->flying_transfers, struct usbi_transfer, list) == itransfer);
+       list_del(&itransfer->list);
+       if (rearm_timer)
+               r = arm_timer_for_next_timeout(ctx);
        usbi_mutex_unlock(&ctx->flying_transfers_lock);
 
        return r;
@@ -1462,7 +1484,7 @@ static int remove_from_flying_list(struct usbi_transfer *transfer)
  * \returns LIBUSB_ERROR_NOT_SUPPORTED if the transfer flags are not supported
  * by the operating system.
  * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than
- * the operating system and/or hardware can support
+ * the operating system and/or hardware can support (see \ref asynclimits)
  * \returns another LIBUSB_ERROR code on other failure
  */
 int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer)
@@ -1645,7 +1667,7 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer,
 
        r = remove_from_flying_list(itransfer);
        if (r < 0)
-               usbi_err(ITRANSFER_CTX(itransfer), "failed to set timer for next timeout, errno=%d", errno);
+               usbi_err(ITRANSFER_CTX(itransfer), "failed to set timer for next timeout");
 
        usbi_mutex_lock(&itransfer->lock);
        itransfer->state_flags &= ~USBI_TRANSFER_IN_FLIGHT;
@@ -1682,39 +1704,44 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer,
  * Do not call this function with the usbi_transfer lock held. User-specified
  * callback functions may attempt to directly resubmit the transfer, which
  * will attempt to take the lock. */
-int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer)
+int usbi_handle_transfer_cancellation(struct usbi_transfer *itransfer)
 {
-       struct libusb_context *ctx = ITRANSFER_CTX(transfer);
+       struct libusb_context *ctx = ITRANSFER_CTX(itransfer);
        uint8_t timed_out;
 
        usbi_mutex_lock(&ctx->flying_transfers_lock);
-       timed_out = transfer->timeout_flags & USBI_TRANSFER_TIMED_OUT;
+       timed_out = itransfer->timeout_flags & USBI_TRANSFER_TIMED_OUT;
        usbi_mutex_unlock(&ctx->flying_transfers_lock);
 
        /* if the URB was cancelled due to timeout, report timeout to the user */
        if (timed_out) {
                usbi_dbg("detected timeout cancellation");
-               return usbi_handle_transfer_completion(transfer, LIBUSB_TRANSFER_TIMED_OUT);
+               return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_TIMED_OUT);
        }
 
        /* otherwise its a normal async cancel */
-       return usbi_handle_transfer_completion(transfer, LIBUSB_TRANSFER_CANCELLED);
+       return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_CANCELLED);
 }
 
 /* Add a completed transfer to the completed_transfers list of the
  * context and signal the event. The backend's handle_transfer_completion()
  * function will be called the next time an event handler runs. */
-void usbi_signal_transfer_completion(struct usbi_transfer *transfer)
+void usbi_signal_transfer_completion(struct usbi_transfer *itransfer)
 {
-       struct libusb_context *ctx = ITRANSFER_CTX(transfer);
-       int pending_events;
+       libusb_device_handle *dev_handle = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->dev_handle;
 
-       usbi_mutex_lock(&ctx->event_data_lock);
-       pending_events = usbi_pending_events(ctx);
-       list_add_tail(&transfer->completed_list, &ctx->completed_transfers);
-       if (!pending_events)
-               usbi_signal_event(ctx);
-       usbi_mutex_unlock(&ctx->event_data_lock);
+       if (dev_handle) {
+               struct libusb_context *ctx = HANDLE_CTX(dev_handle);
+               unsigned int event_flags;
+
+               usbi_mutex_lock(&ctx->event_data_lock);
+               event_flags = ctx->event_flags;
+               ctx->event_flags |= USBI_EVENT_TRANSFER_COMPLETED;
+               list_add_tail(&itransfer->completed_list, &ctx->completed_transfers);
+               if (!event_flags)
+                       usbi_signal_event(&ctx->event);
+               usbi_mutex_unlock(&ctx->event_data_lock);
+       }
 }
 
 /** \ingroup libusb_poll
@@ -1740,7 +1767,8 @@ int API_EXPORTED libusb_try_lock_events(libusb_context *ctx)
 {
        int r;
        unsigned int ru;
-       USBI_GET_CONTEXT(ctx);
+
+       ctx = usbi_get_context(ctx);
 
        /* is someone else waiting to close a device? if so, don't let this thread
         * start event handling */
@@ -1753,7 +1781,7 @@ int API_EXPORTED libusb_try_lock_events(libusb_context *ctx)
        }
 
        r = usbi_mutex_trylock(&ctx->events_lock);
-       if (r)
+       if (!r)
                return 1;
 
        ctx->event_handler_active = 1;
@@ -1780,7 +1808,7 @@ int API_EXPORTED libusb_try_lock_events(libusb_context *ctx)
  */
 void API_EXPORTED libusb_lock_events(libusb_context *ctx)
 {
-       USBI_GET_CONTEXT(ctx);
+       ctx = usbi_get_context(ctx);
        usbi_mutex_lock(&ctx->events_lock);
        ctx->event_handler_active = 1;
 }
@@ -1795,7 +1823,7 @@ void API_EXPORTED libusb_lock_events(libusb_context *ctx)
  */
 void API_EXPORTED libusb_unlock_events(libusb_context *ctx)
 {
-       USBI_GET_CONTEXT(ctx);
+       ctx = usbi_get_context(ctx);
        ctx->event_handler_active = 0;
        usbi_mutex_unlock(&ctx->events_lock);
 
@@ -1831,7 +1859,8 @@ void API_EXPORTED libusb_unlock_events(libusb_context *ctx)
 int API_EXPORTED libusb_event_handling_ok(libusb_context *ctx)
 {
        unsigned int r;
-       USBI_GET_CONTEXT(ctx);
+
+       ctx = usbi_get_context(ctx);
 
        /* is someone else waiting to close a device? if so, don't let this thread
         * continue event handling */
@@ -1859,7 +1888,8 @@ int API_EXPORTED libusb_event_handling_ok(libusb_context *ctx)
 int API_EXPORTED libusb_event_handler_active(libusb_context *ctx)
 {
        unsigned int r;
-       USBI_GET_CONTEXT(ctx);
+
+       ctx = usbi_get_context(ctx);
 
        /* is someone else waiting to close a device? if so, don't let this thread
         * start event handling -- indicate that event handling is happening */
@@ -1886,16 +1916,17 @@ int API_EXPORTED libusb_event_handler_active(libusb_context *ctx)
  */
 void API_EXPORTED libusb_interrupt_event_handler(libusb_context *ctx)
 {
-       int pending_events;
-       USBI_GET_CONTEXT(ctx);
+       unsigned int event_flags;
 
-       usbi_dbg("");
+       usbi_dbg(" ");
+
+       ctx = usbi_get_context(ctx);
        usbi_mutex_lock(&ctx->event_data_lock);
 
-       pending_events = usbi_pending_events(ctx);
+       event_flags = ctx->event_flags;
        ctx->event_flags |= USBI_EVENT_USER_INTERRUPT;
-       if (!pending_events)
-               usbi_signal_event(ctx);
+       if (!event_flags)
+               usbi_signal_event(&ctx->event);
 
        usbi_mutex_unlock(&ctx->event_data_lock);
 }
@@ -1911,7 +1942,7 @@ void API_EXPORTED libusb_interrupt_event_handler(libusb_context *ctx)
  *
  * You only need to use this lock if you are developing an application
  * which calls poll() or select() on libusb's file descriptors directly,
- * <b>and</b> may potentially be handling events from 2 threads simultaenously.
+ * <b>and</b> may potentially be handling events from 2 threads simultaneously.
  * If you stick to libusb's event handling loop functions (e.g.
  * libusb_handle_events()) then you do not need to be concerned with this
  * locking.
@@ -1921,7 +1952,7 @@ void API_EXPORTED libusb_interrupt_event_handler(libusb_context *ctx)
  */
 void API_EXPORTED libusb_lock_event_waiters(libusb_context *ctx)
 {
-       USBI_GET_CONTEXT(ctx);
+       ctx = usbi_get_context(ctx);
        usbi_mutex_lock(&ctx->event_waiters_lock);
 }
 
@@ -1932,7 +1963,7 @@ void API_EXPORTED libusb_lock_event_waiters(libusb_context *ctx)
  */
 void API_EXPORTED libusb_unlock_event_waiters(libusb_context *ctx)
 {
-       USBI_GET_CONTEXT(ctx);
+       ctx = usbi_get_context(ctx);
        usbi_mutex_unlock(&ctx->event_waiters_lock);
 }
 
@@ -1959,25 +1990,28 @@ void API_EXPORTED libusb_unlock_event_waiters(libusb_context *ctx)
  * indicates unlimited timeout.
  * \returns 0 after a transfer completes or another thread stops event handling
  * \returns 1 if the timeout expired
+ * \returns LIBUSB_ERROR_INVALID_PARAM if timeval is invalid
  * \ref libusb_mtasync
  */
 int API_EXPORTED libusb_wait_for_event(libusb_context *ctx, struct timeval *tv)
 {
        int r;
 
-       USBI_GET_CONTEXT(ctx);
-       if (tv == NULL) {
+       ctx = usbi_get_context(ctx);
+       if (!tv) {
                usbi_cond_wait(&ctx->event_waiters_cond, &ctx->event_waiters_lock);
                return 0;
        }
 
+       if (!TIMEVAL_IS_VALID(tv))
+               return LIBUSB_ERROR_INVALID_PARAM;
+
        r = usbi_cond_timedwait(&ctx->event_waiters_cond,
                &ctx->event_waiters_lock, tv);
-
        if (r < 0)
-               return r;
-       else
-               return (r == ETIMEDOUT);
+               return r == LIBUSB_ERROR_TIMEOUT;
+
+       return 0;
 }
 
 static void handle_timeout(struct usbi_transfer *itransfer)
@@ -1992,78 +2026,149 @@ static void handle_timeout(struct usbi_transfer *itransfer)
                itransfer->timeout_flags |= USBI_TRANSFER_TIMED_OUT;
        else
                usbi_warn(TRANSFER_CTX(transfer),
-                       "async cancel failed %d errno=%d", r, errno);
+                       "async cancel failed %d", r);
 }
 
-static int handle_timeouts_locked(struct libusb_context *ctx)
+static void handle_timeouts_locked(struct libusb_context *ctx)
 {
-       int r;
-       struct timespec systime_ts;
-       struct timeval systime;
-       struct usbi_transfer *transfer;
+       struct timespec systime;
+       struct usbi_transfer *itransfer;
 
        if (list_empty(&ctx->flying_transfers))
-               return 0;
+               return;
 
        /* get current time */
-       r = usbi_backend.clock_gettime(USBI_CLOCK_MONOTONIC, &systime_ts);
-       if (r < 0)
-               return r;
-
-       TIMESPEC_TO_TIMEVAL(&systime, &systime_ts);
+       usbi_get_monotonic_time(&systime);
 
        /* iterate through flying transfers list, finding all transfers that
         * have expired timeouts */
-       list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) {
-               struct timeval *cur_tv = &transfer->timeout;
+       for_each_transfer(ctx, itransfer) {
+               struct timespec *cur_ts = &itransfer->timeout;
 
                /* if we've reached transfers of infinite timeout, we're all done */
-               if (!timerisset(cur_tv))
-                       return 0;
+               if (!TIMESPEC_IS_SET(cur_ts))
+                       return;
 
                /* ignore timeouts we've already handled */
-               if (transfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))
+               if (itransfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))
                        continue;
 
                /* if transfer has non-expired timeout, nothing more to do */
-               if ((cur_tv->tv_sec > systime.tv_sec) ||
-                               (cur_tv->tv_sec == systime.tv_sec &&
-                                       cur_tv->tv_usec > systime.tv_usec))
-                       return 0;
+               if (TIMESPEC_CMP(cur_ts, &systime, >))
+                       return;
 
                /* otherwise, we've got an expired timeout to handle */
-               handle_timeout(transfer);
+               handle_timeout(itransfer);
        }
-       return 0;
 }
 
-static int handle_timeouts(struct libusb_context *ctx)
+static void handle_timeouts(struct libusb_context *ctx)
 {
-       int r;
-       USBI_GET_CONTEXT(ctx);
+       ctx = usbi_get_context(ctx);
        usbi_mutex_lock(&ctx->flying_transfers_lock);
-       r = handle_timeouts_locked(ctx);
+       handle_timeouts_locked(ctx);
        usbi_mutex_unlock(&ctx->flying_transfers_lock);
+}
+
+static int handle_event_trigger(struct libusb_context *ctx)
+{
+       struct list_head hotplug_msgs;
+       int r = 0;
+
+       usbi_dbg("event triggered");
+
+       list_init(&hotplug_msgs);
+
+       /* take the the event data lock while processing events */
+       usbi_mutex_lock(&ctx->event_data_lock);
+
+       /* check if someone modified the event sources */
+       if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED)
+               usbi_dbg("someone updated the event sources");
+
+       if (ctx->event_flags & USBI_EVENT_USER_INTERRUPT) {
+               usbi_dbg("someone purposefully interrupted");
+               ctx->event_flags &= ~USBI_EVENT_USER_INTERRUPT;
+       }
+
+       /* check if someone is closing a device */
+       if (ctx->event_flags & USBI_EVENT_DEVICE_CLOSE)
+               usbi_dbg("someone is closing a device");
+
+       /* check for any pending hotplug messages */
+       if (ctx->event_flags & USBI_EVENT_HOTPLUG_MSG_PENDING) {
+               usbi_dbg("hotplug message received");
+               ctx->event_flags &= ~USBI_EVENT_HOTPLUG_MSG_PENDING;
+               assert(!list_empty(&ctx->hotplug_msgs));
+               list_cut(&hotplug_msgs, &ctx->hotplug_msgs);
+       }
+
+       /* complete any pending transfers */
+       if (ctx->event_flags & USBI_EVENT_TRANSFER_COMPLETED) {
+               struct usbi_transfer *itransfer, *tmp;
+               struct list_head completed_transfers;
+
+               assert(!list_empty(&ctx->completed_transfers));
+               list_cut(&completed_transfers, &ctx->completed_transfers);
+               usbi_mutex_unlock(&ctx->event_data_lock);
+
+               __for_each_completed_transfer_safe(&completed_transfers, itransfer, tmp) {
+                       list_del(&itransfer->completed_list);
+                       r = usbi_backend.handle_transfer_completion(itransfer);
+                       if (r) {
+                               usbi_err(ctx, "backend handle_transfer_completion failed with error %d", r);
+                               break;
+                       }
+               }
+
+               usbi_mutex_lock(&ctx->event_data_lock);
+               if (!list_empty(&completed_transfers)) {
+                       /* an error occurred, put the remaining transfers back on the list */
+                       list_splice_front(&completed_transfers, &ctx->completed_transfers);
+               } else if (list_empty(&ctx->completed_transfers)) {
+                       ctx->event_flags &= ~USBI_EVENT_TRANSFER_COMPLETED;
+               }
+       }
+
+       /* if no further pending events, clear the event */
+       if (!ctx->event_flags)
+               usbi_clear_event(&ctx->event);
+
+       usbi_mutex_unlock(&ctx->event_data_lock);
+
+       /* process the hotplug messages, if any */
+       while (!list_empty(&hotplug_msgs)) {
+               struct libusb_hotplug_message *message =
+                       list_first_entry(&hotplug_msgs, struct libusb_hotplug_message, list);
+
+               usbi_hotplug_match(ctx, message->device, message->event);
+
+               /* the device left, dereference the device */
+               if (message->event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
+                       libusb_unref_device(message->device);
+
+               list_del(&message->list);
+               free(message);
+       }
+
        return r;
 }
 
-#ifdef USBI_TIMERFD_AVAILABLE
-static int handle_timerfd_trigger(struct libusb_context *ctx)
+#ifdef HAVE_OS_TIMER
+static int handle_timer_trigger(struct libusb_context *ctx)
 {
        int r;
 
        usbi_mutex_lock(&ctx->flying_transfers_lock);
 
        /* process the timeout that just happened */
-       r = handle_timeouts_locked(ctx);
-       if (r < 0)
-               goto out;
+       handle_timeouts_locked(ctx);
 
-       /* arm for next timeout*/
-       r = arm_timerfd_for_next_timeout(ctx);
+       /* arm for next timeout */
+       r = arm_timer_for_next_timeout(ctx);
 
-out:
        usbi_mutex_unlock(&ctx->flying_transfers_lock);
+
        return r;
 }
 #endif
@@ -2072,73 +2177,38 @@ out:
  * doing the same thing. */
 static int handle_events(struct libusb_context *ctx, struct timeval *tv)
 {
-       int r;
-       struct usbi_pollfd *ipollfd;
-       POLL_NFDS_TYPE nfds = 0;
-       POLL_NFDS_TYPE internal_nfds;
-       struct pollfd *fds = NULL;
-       int i = -1;
-       int timeout_ms;
+       struct usbi_reported_events reported_events;
+       int r, timeout_ms;
 
        /* prevent attempts to recursively handle events (e.g. calling into
         * libusb_handle_events() from within a hotplug or transfer callback) */
        if (usbi_handling_events(ctx))
                return LIBUSB_ERROR_BUSY;
-       usbi_start_event_handling(ctx);
 
-       /* there are certain fds that libusb uses internally, currently:
-        *
-        *   1) event pipe
-        *   2) timerfd
-        *
-        * the backend will never need to attempt to handle events on these fds, so
-        * we determine how many fds are in use internally for this context and when
-        * handle_events() is called in the backend, the pollfd list and count will
-        * be adjusted to skip over these internal fds */
-       if (usbi_using_timerfd(ctx))
-               internal_nfds = 2;
-       else
-               internal_nfds = 1;
-
-       /* only reallocate the poll fds when the list of poll fds has been modified
-        * since the last poll, otherwise reuse them to save the additional overhead */
+       /* only reallocate the event source data when the list of event sources has
+        * been modified since the last handle_events(), otherwise reuse them to
+        * save the additional overhead */
        usbi_mutex_lock(&ctx->event_data_lock);
-       if (ctx->event_flags & USBI_EVENT_POLLFDS_MODIFIED) {
-               usbi_dbg("poll fds modified, reallocating");
+       if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED) {
+               usbi_dbg("event sources modified, reallocating event data");
 
-               if (ctx->pollfds) {
-                       free(ctx->pollfds);
-                       ctx->pollfds = NULL;
-               }
-
-               /* sanity check - it is invalid for a context to have fewer than the
-                * required internal fds (memory corruption?) */
-               assert(ctx->pollfds_cnt >= internal_nfds);
+               /* free anything removed since we last ran */
+               cleanup_removed_event_sources(ctx);
 
-               ctx->pollfds = calloc(ctx->pollfds_cnt, sizeof(*ctx->pollfds));
-               if (!ctx->pollfds) {
+               r = usbi_alloc_event_data(ctx);
+               if (r) {
                        usbi_mutex_unlock(&ctx->event_data_lock);
-                       r = LIBUSB_ERROR_NO_MEM;
-                       goto done;
-               }
-
-               list_for_each_entry(ipollfd, &ctx->ipollfds, list, struct usbi_pollfd) {
-                       struct libusb_pollfd *pollfd = &ipollfd->pollfd;
-                       i++;
-                       ctx->pollfds[i].fd = pollfd->fd;
-                       ctx->pollfds[i].events = pollfd->events;
+                       return r;
                }
 
                /* reset the flag now that we have the updated list */
-               ctx->event_flags &= ~USBI_EVENT_POLLFDS_MODIFIED;
+               ctx->event_flags &= ~USBI_EVENT_EVENT_SOURCES_MODIFIED;
 
-               /* if no further pending events, clear the event pipe so that we do
-                * not immediately return from poll */
-               if (!usbi_pending_events(ctx))
-                       usbi_clear_event(ctx);
+               /* if no further pending events, clear the event so that we do
+                * not immediately return from the wait function */
+               if (!ctx->event_flags)
+                       usbi_clear_event(&ctx->event);
        }
-       fds = ctx->pollfds;
-       nfds = ctx->pollfds_cnt;
        usbi_mutex_unlock(&ctx->event_data_lock);
 
        timeout_ms = (int)(tv->tv_sec * 1000) + (tv->tv_usec / 1000);
@@ -2147,125 +2217,42 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv)
        if (tv->tv_usec % 1000)
                timeout_ms++;
 
-       usbi_dbg("poll() %d fds with timeout in %dms", nfds, timeout_ms);
-       r = usbi_poll(fds, nfds, timeout_ms);
-       usbi_dbg("poll() returned %d", r);
-       if (r == 0) {
-               r = handle_timeouts(ctx);
-               goto done;
-       } else if (r == -1 && errno == EINTR) {
-               r = LIBUSB_ERROR_INTERRUPTED;
-               goto done;
-       } else if (r < 0) {
-               usbi_err(ctx, "poll failed %d err=%d", r, errno);
-               r = LIBUSB_ERROR_IO;
-               goto done;
-       }
-
-       /* fds[0] is always the event pipe */
-       if (fds[0].revents) {
-               struct list_head hotplug_msgs;
-               struct usbi_transfer *itransfer;
-               int hotplug_cb_deregistered = 0;
-               int ret = 0;
-
-               list_init(&hotplug_msgs);
-
-               usbi_dbg("caught a fish on the event pipe");
-
-               /* take the the event data lock while processing events */
-               usbi_mutex_lock(&ctx->event_data_lock);
-
-               /* check if someone added a new poll fd */
-               if (ctx->event_flags & USBI_EVENT_POLLFDS_MODIFIED)
-                       usbi_dbg("someone updated the poll fds");
+       reported_events.event_bits = 0;
 
-               if (ctx->event_flags & USBI_EVENT_USER_INTERRUPT) {
-                       usbi_dbg("someone purposely interrupted");
-                       ctx->event_flags &= ~USBI_EVENT_USER_INTERRUPT;
-               }
-
-               if (ctx->event_flags & USBI_EVENT_HOTPLUG_CB_DEREGISTERED) {
-                       usbi_dbg("someone unregistered a hotplug cb");
-                       ctx->event_flags &= ~USBI_EVENT_HOTPLUG_CB_DEREGISTERED;
-                       hotplug_cb_deregistered = 1;
-               }
-
-               /* check if someone is closing a device */
-               if (ctx->device_close)
-                       usbi_dbg("someone is closing a device");
-
-               /* check for any pending hotplug messages */
-               if (!list_empty(&ctx->hotplug_msgs)) {
-                       usbi_dbg("hotplug message received");
-                       list_cut(&hotplug_msgs, &ctx->hotplug_msgs);
-               }
-
-               /* complete any pending transfers */
-               while (ret == 0 && !list_empty(&ctx->completed_transfers)) {
-                       itransfer = list_first_entry(&ctx->completed_transfers, struct usbi_transfer, completed_list);
-                       list_del(&itransfer->completed_list);
-                       usbi_mutex_unlock(&ctx->event_data_lock);
-                       ret = usbi_backend.handle_transfer_completion(itransfer);
-                       if (ret)
-                               usbi_err(ctx, "backend handle_transfer_completion failed with error %d", ret);
-                       usbi_mutex_lock(&ctx->event_data_lock);
-               }
-
-               /* if no further pending events, clear the event pipe */
-               if (!usbi_pending_events(ctx))
-                       usbi_clear_event(ctx);
-
-               usbi_mutex_unlock(&ctx->event_data_lock);
-
-               if (hotplug_cb_deregistered)
-                       usbi_hotplug_deregister(ctx, 0);
-
-               /* process the hotplug messages, if any */
-               while (!list_empty(&hotplug_msgs)) {
-                       struct libusb_hotplug_message *message =
-                               list_first_entry(&hotplug_msgs, struct libusb_hotplug_message, list);
-
-                       usbi_hotplug_match(ctx, message->device, message->event);
-
-                       /* the device left, dereference the device */
-                       if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == message->event)
-                               libusb_unref_device(message->device);
+       usbi_start_event_handling(ctx);
 
-                       list_del(&message->list);
-                       free(message);
+       r = usbi_wait_for_events(ctx, &reported_events, timeout_ms);
+       if (r != LIBUSB_SUCCESS) {
+               if (r == LIBUSB_ERROR_TIMEOUT) {
+                       handle_timeouts(ctx);
+                       r = LIBUSB_SUCCESS;
                }
+               goto done;
+       }
 
-               if (ret) {
+       if (reported_events.event_triggered) {
+               r = handle_event_trigger(ctx);
+               if (r) {
                        /* return error code */
-                       r = ret;
                        goto done;
                }
-
-               if (0 == --r)
-                       goto done;
        }
 
-#ifdef USBI_TIMERFD_AVAILABLE
-       /* on timerfd configurations, fds[1] is the timerfd */
-       if (usbi_using_timerfd(ctx) && fds[1].revents) {
-               /* timerfd indicates that a timeout has expired */
-               int ret;
-               usbi_dbg("timerfd triggered");
-
-               ret = handle_timerfd_trigger(ctx);
-               if (ret < 0) {
+#ifdef HAVE_OS_TIMER
+       if (reported_events.timer_triggered) {
+               r = handle_timer_trigger(ctx);
+               if (r) {
                        /* return error code */
-                       r = ret;
                        goto done;
                }
-
-               if (0 == --r)
-                       goto done;
        }
 #endif
 
-       r = usbi_backend.handle_events(ctx, fds + internal_nfds, nfds - internal_nfds, r);
+       if (!reported_events.num_ready)
+               goto done;
+
+       r = usbi_backend.handle_events(ctx, reported_events.event_data,
+               reported_events.event_data_count, reported_events.num_ready);
        if (r)
                usbi_err(ctx, "backend handle_events failed with error %d", r);
 
@@ -2324,7 +2311,9 @@ static int get_next_timeout(libusb_context *ctx, struct timeval *tv,
  * \param tv the maximum time to block waiting for events, or an all zero
  * timeval struct for non-blocking mode
  * \param completed pointer to completion integer to check, or NULL
- * \returns 0 on success, or a LIBUSB_ERROR code on failure
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_INVALID_PARAM if timeval is invalid
+ * \returns another LIBUSB_ERROR code on other failure
  * \ref libusb_mtasync
  */
 int API_EXPORTED libusb_handle_events_timeout_completed(libusb_context *ctx,
@@ -2333,11 +2322,15 @@ int API_EXPORTED libusb_handle_events_timeout_completed(libusb_context *ctx,
        int r;
        struct timeval poll_timeout;
 
-       USBI_GET_CONTEXT(ctx);
+       if (!TIMEVAL_IS_VALID(tv))
+               return LIBUSB_ERROR_INVALID_PARAM;
+
+       ctx = usbi_get_context(ctx);
        r = get_next_timeout(ctx, tv, &poll_timeout);
        if (r) {
                /* timeout already expired */
-               return handle_timeouts(ctx);
+               handle_timeouts(ctx);
+               return 0;
        }
 
 retry:
@@ -2375,9 +2368,8 @@ already_done:
        if (r < 0)
                return r;
        else if (r == 1)
-               return handle_timeouts(ctx);
-       else
-               return 0;
+               handle_timeouts(ctx);
+       return 0;
 }
 
 /** \ingroup libusb_poll
@@ -2404,7 +2396,7 @@ int API_EXPORTED libusb_handle_events_timeout(libusb_context *ctx,
 
 /** \ingroup libusb_poll
  * Handle any pending events in blocking mode. There is currently a timeout
- * hardcoded at 60 seconds but we plan to make it unlimited in future. For
+ * hard-coded at 60 seconds but we plan to make it unlimited in future. For
  * finer control over whether this function is blocking or non-blocking, or
  * for control over the timeout, use libusb_handle_events_timeout_completed()
  * instead.
@@ -2461,7 +2453,9 @@ int API_EXPORTED libusb_handle_events_completed(libusb_context *ctx,
  * \param ctx the context to operate on, or NULL for the default context
  * \param tv the maximum time to block waiting for events, or zero for
  * non-blocking mode
- * \returns 0 on success, or a LIBUSB_ERROR code on failure
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_INVALID_PARAM if timeval is invalid
+ * \returns another LIBUSB_ERROR code on other failure
  * \ref libusb_mtasync
  */
 int API_EXPORTED libusb_handle_events_locked(libusb_context *ctx,
@@ -2470,11 +2464,15 @@ int API_EXPORTED libusb_handle_events_locked(libusb_context *ctx,
        int r;
        struct timeval poll_timeout;
 
-       USBI_GET_CONTEXT(ctx);
+       if (!TIMEVAL_IS_VALID(tv))
+               return LIBUSB_ERROR_INVALID_PARAM;
+
+       ctx = usbi_get_context(ctx);
        r = get_next_timeout(ctx, tv, &poll_timeout);
        if (r) {
                /* timeout already expired */
-               return handle_timeouts(ctx);
+               handle_timeouts(ctx);
+               return 0;
        }
 
        return handle_events(ctx, &poll_timeout);
@@ -2510,13 +2508,8 @@ int API_EXPORTED libusb_handle_events_locked(libusb_context *ctx,
  */
 int API_EXPORTED libusb_pollfds_handle_timeouts(libusb_context *ctx)
 {
-#if defined(USBI_TIMERFD_AVAILABLE)
-       USBI_GET_CONTEXT(ctx);
-       return usbi_using_timerfd(ctx);
-#else
-       UNUSED(ctx);
-       return 0;
-#endif
+       ctx = usbi_get_context(ctx);
+       return usbi_using_timer(ctx);
 }
 
 /** \ingroup libusb_poll
@@ -2550,14 +2543,12 @@ int API_EXPORTED libusb_pollfds_handle_timeouts(libusb_context *ctx)
 int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx,
        struct timeval *tv)
 {
-       struct usbi_transfer *transfer;
-       struct timespec cur_ts;
-       struct timeval cur_tv;
-       struct timeval next_timeout = { 0, 0 };
-       int r;
+       struct usbi_transfer *itransfer;
+       struct timespec systime;
+       struct timespec next_timeout = { 0, 0 };
 
-       USBI_GET_CONTEXT(ctx);
-       if (usbi_using_timerfd(ctx))
+       ctx = usbi_get_context(ctx);
+       if (usbi_using_timer(ctx))
                return 0;
 
        usbi_mutex_lock(&ctx->flying_transfers_lock);
@@ -2568,37 +2559,33 @@ int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx,
        }
 
        /* find next transfer which hasn't already been processed as timed out */
-       list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) {
-               if (transfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))
+       for_each_transfer(ctx, itransfer) {
+               if (itransfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))
                        continue;
 
-               /* if we've reached transfers of infinte timeout, we're done looking */
-               if (!timerisset(&transfer->timeout))
+               /* if we've reached transfers of infinite timeout, we're done looking */
+               if (!TIMESPEC_IS_SET(&itransfer->timeout))
                        break;
 
-               next_timeout = transfer->timeout;
+               next_timeout = itransfer->timeout;
                break;
        }
        usbi_mutex_unlock(&ctx->flying_transfers_lock);
 
-       if (!timerisset(&next_timeout)) {
+       if (!TIMESPEC_IS_SET(&next_timeout)) {
                usbi_dbg("no URB with timeout or all handled by OS; no timeout!");
                return 0;
        }
 
-       r = usbi_backend.clock_gettime(USBI_CLOCK_MONOTONIC, &cur_ts);
-       if (r < 0) {
-               usbi_err(ctx, "failed to read monotonic clock, errno=%d", errno);
-               return 0;
-       }
-       TIMESPEC_TO_TIMEVAL(&cur_tv, &cur_ts);
+       usbi_get_monotonic_time(&systime);
 
-       if (!timercmp(&cur_tv, &next_timeout, <)) {
+       if (!TIMESPEC_CMP(&systime, &next_timeout, <)) {
                usbi_dbg("first timeout already expired");
                timerclear(tv);
        } else {
-               timersub(&next_timeout, &cur_tv, tv);
-               usbi_dbg("next timeout in %d.%06ds", tv->tv_sec, tv->tv_usec);
+               TIMESPEC_SUB(&next_timeout, &systime, &next_timeout);
+               TIMESPEC_TO_TIMEVAL(tv, &next_timeout);
+               usbi_dbg("next timeout in %ld.%06lds", (long)tv->tv_sec, (long)tv->tv_usec);
        }
 
        return 1;
@@ -2629,78 +2616,92 @@ void API_EXPORTED libusb_set_pollfd_notifiers(libusb_context *ctx,
        libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb,
        void *user_data)
 {
-       USBI_GET_CONTEXT(ctx);
+#if !defined(PLATFORM_WINDOWS)
+       ctx = usbi_get_context(ctx);
        ctx->fd_added_cb = added_cb;
        ctx->fd_removed_cb = removed_cb;
        ctx->fd_cb_user_data = user_data;
+#else
+       usbi_err(ctx, "external polling of libusb's internal event sources " \
+               "is not yet supported on Windows");
+       UNUSED(added_cb);
+       UNUSED(removed_cb);
+       UNUSED(user_data);
+#endif
 }
 
 /*
  * Interrupt the iteration of the event handling thread, so that it picks
- * up the fd change. Callers of this function must hold the event_data_lock.
+ * up the event source change. Callers of this function must hold the event_data_lock.
  */
-static void usbi_fd_notification(struct libusb_context *ctx)
+static void usbi_event_source_notification(struct libusb_context *ctx)
 {
-       int pending_events;
+       unsigned int event_flags;
 
        /* Record that there is a new poll fd.
         * Only signal an event if there are no prior pending events. */
-       pending_events = usbi_pending_events(ctx);
-       ctx->event_flags |= USBI_EVENT_POLLFDS_MODIFIED;
-       if (!pending_events)
-               usbi_signal_event(ctx);
+       event_flags = ctx->event_flags;
+       ctx->event_flags |= USBI_EVENT_EVENT_SOURCES_MODIFIED;
+       if (!event_flags)
+               usbi_signal_event(&ctx->event);
 }
 
-/* Add a file descriptor to the list of file descriptors to be monitored.
- * events should be specified as a bitmask of events passed to poll(), e.g.
+/* Add an event source to the list of event sources to be monitored.
+ * poll_events should be specified as a bitmask of events passed to poll(), e.g.
  * POLLIN and/or POLLOUT. */
-int usbi_add_pollfd(struct libusb_context *ctx, int fd, short events)
+int usbi_add_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle, short poll_events)
 {
-       struct usbi_pollfd *ipollfd = malloc(sizeof(*ipollfd));
-       if (!ipollfd)
+       struct usbi_event_source *ievent_source = malloc(sizeof(*ievent_source));
+
+       if (!ievent_source)
                return LIBUSB_ERROR_NO_MEM;
 
-       usbi_dbg("add fd %d events %d", fd, events);
-       ipollfd->pollfd.fd = fd;
-       ipollfd->pollfd.events = events;
+       usbi_dbg("add " USBI_OS_HANDLE_FORMAT_STRING " events %d", os_handle, poll_events);
+       ievent_source->data.os_handle = os_handle;
+       ievent_source->data.poll_events = poll_events;
        usbi_mutex_lock(&ctx->event_data_lock);
-       list_add_tail(&ipollfd->list, &ctx->ipollfds);
-       ctx->pollfds_cnt++;
-       usbi_fd_notification(ctx);
+       list_add_tail(&ievent_source->list, &ctx->event_sources);
+       usbi_event_source_notification(ctx);
        usbi_mutex_unlock(&ctx->event_data_lock);
 
+#if !defined(PLATFORM_WINDOWS)
        if (ctx->fd_added_cb)
-               ctx->fd_added_cb(fd, events, ctx->fd_cb_user_data);
+               ctx->fd_added_cb(os_handle, poll_events, ctx->fd_cb_user_data);
+#endif
+
        return 0;
 }
 
-/* Remove a file descriptor from the list of file descriptors to be polled. */
-void usbi_remove_pollfd(struct libusb_context *ctx, int fd)
+/* Remove an event source from the list of event sources to be monitored. */
+void usbi_remove_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle)
 {
-       struct usbi_pollfd *ipollfd;
+       struct usbi_event_source *ievent_source;
        int found = 0;
 
-       usbi_dbg("remove fd %d", fd);
+       usbi_dbg("remove " USBI_OS_HANDLE_FORMAT_STRING, os_handle);
        usbi_mutex_lock(&ctx->event_data_lock);
-       list_for_each_entry(ipollfd, &ctx->ipollfds, list, struct usbi_pollfd)
-               if (ipollfd->pollfd.fd == fd) {
+       for_each_event_source(ctx, ievent_source) {
+               if (ievent_source->data.os_handle == os_handle) {
                        found = 1;
                        break;
                }
+       }
 
        if (!found) {
-               usbi_dbg("couldn't find fd %d to remove", fd);
+               usbi_dbg("couldn't find " USBI_OS_HANDLE_FORMAT_STRING " to remove", os_handle);
                usbi_mutex_unlock(&ctx->event_data_lock);
                return;
        }
 
-       list_del(&ipollfd->list);
-       ctx->pollfds_cnt--;
-       usbi_fd_notification(ctx);
+       list_del(&ievent_source->list);
+       list_add_tail(&ievent_source->list, &ctx->removed_event_sources);
+       usbi_event_source_notification(ctx);
        usbi_mutex_unlock(&ctx->event_data_lock);
-       free(ipollfd);
+
+#if !defined(PLATFORM_WINDOWS)
        if (ctx->fd_removed_cb)
-               ctx->fd_removed_cb(fd, ctx->fd_cb_user_data);
+               ctx->fd_removed_cb(os_handle, ctx->fd_cb_user_data);
+#endif
 }
 
 /** \ingroup libusb_poll
@@ -2722,28 +2723,36 @@ DEFAULT_VISIBILITY
 const struct libusb_pollfd ** LIBUSB_CALL libusb_get_pollfds(
        libusb_context *ctx)
 {
-#ifndef OS_WINDOWS
+#if !defined(PLATFORM_WINDOWS)
        struct libusb_pollfd **ret = NULL;
-       struct usbi_pollfd *ipollfd;
-       size_t i = 0;
-       USBI_GET_CONTEXT(ctx);
+       struct usbi_event_source *ievent_source;
+       size_t i;
+
+       static_assert(sizeof(struct usbi_event_source_data) == sizeof(struct libusb_pollfd),
+                     "mismatch between usbi_event_source_data and libusb_pollfd sizes");
+
+       ctx = usbi_get_context(ctx);
 
        usbi_mutex_lock(&ctx->event_data_lock);
 
-       ret = calloc(ctx->pollfds_cnt + 1, sizeof(struct libusb_pollfd *));
+       i = 0;
+       for_each_event_source(ctx, ievent_source)
+               i++;
+
+       ret = calloc(i + 1, sizeof(struct libusb_pollfd *));
        if (!ret)
                goto out;
 
-       list_for_each_entry(ipollfd, &ctx->ipollfds, list, struct usbi_pollfd)
-               ret[i++] = (struct libusb_pollfd *) ipollfd;
-       ret[ctx->pollfds_cnt] = NULL;
+       i = 0;
+       for_each_event_source(ctx, ievent_source)
+               ret[i++] = (struct libusb_pollfd *)ievent_source;
 
 out:
        usbi_mutex_unlock(&ctx->event_data_lock);
-       return (const struct libusb_pollfd **) ret;
+       return (const struct libusb_pollfd **)ret;
 #else
-       usbi_err(ctx, "external polling of libusb's internal descriptors "\
-               "is not yet supported on Windows platforms");
+       usbi_err(ctx, "external polling of libusb's internal event sources " \
+               "is not yet supported on Windows");
        return NULL;
 #endif
 }
@@ -2755,16 +2764,17 @@ out:
  * Since version 1.0.20, \ref LIBUSB_API_VERSION >= 0x01000104
  *
  * It is legal to call this function with a NULL pollfd list. In this case,
- * the function will simply return safely.
+ * the function will simply do nothing.
  *
  * \param pollfds the list of libusb_pollfd structures to free
  */
 void API_EXPORTED libusb_free_pollfds(const struct libusb_pollfd **pollfds)
 {
-       if (!pollfds)
-               return;
-
+#if !defined(PLATFORM_WINDOWS)
        free((void *)pollfds);
+#else
+       UNUSED(pollfds);
+#endif
 }
 
 /* Backends may call this from handle_events to report disconnection of a
@@ -2773,6 +2783,7 @@ void API_EXPORTED libusb_free_pollfds(const struct libusb_pollfd **pollfds)
  */
 void usbi_handle_disconnect(struct libusb_device_handle *dev_handle)
 {
+       struct libusb_context *ctx = HANDLE_CTX(dev_handle);
        struct usbi_transfer *cur;
        struct usbi_transfer *to_cancel;
 
@@ -2794,8 +2805,8 @@ void usbi_handle_disconnect(struct libusb_device_handle *dev_handle)
 
        while (1) {
                to_cancel = NULL;
-               usbi_mutex_lock(&HANDLE_CTX(dev_handle)->flying_transfers_lock);
-               list_for_each_entry(cur, &HANDLE_CTX(dev_handle)->flying_transfers, list, struct usbi_transfer)
+               usbi_mutex_lock(&ctx->flying_transfers_lock);
+               for_each_transfer(ctx, cur) {
                        if (USBI_TRANSFER_TO_LIBUSB_TRANSFER(cur)->dev_handle == dev_handle) {
                                usbi_mutex_lock(&cur->lock);
                                if (cur->state_flags & USBI_TRANSFER_IN_FLIGHT)
@@ -2805,7 +2816,8 @@ void usbi_handle_disconnect(struct libusb_device_handle *dev_handle)
                                if (to_cancel)
                                        break;
                        }
-               usbi_mutex_unlock(&HANDLE_CTX(dev_handle)->flying_transfers_lock);
+               }
+               usbi_mutex_unlock(&ctx->flying_transfers_lock);
 
                if (!to_cancel)
                        break;
@@ -2818,5 +2830,4 @@ void usbi_handle_disconnect(struct libusb_device_handle *dev_handle)
                usbi_mutex_unlock(&to_cancel->lock);
                usbi_handle_transfer_completion(to_cancel, LIBUSB_TRANSFER_NO_DEVICE);
        }
-
 }
index 430136b2e28508f2c83292dd152709726d5b01a8..1308571cd60f352437baf4bcee267a5c01f7b3f6 100644 (file)
@@ -3,7 +3,8 @@
  * Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com>
  * Copyright © 2007-2008 Daniel Drake <dsd@gentoo.org>
  * Copyright © 2012 Pete Batard <pete@akeo.ie>
- * Copyright © 2012 Nathan Hjelm <hjelmn@cs.unm.edu>
+ * Copyright © 2012-2018 Nathan Hjelm <hjelmn@cs.unm.edu>
+ * Copyright © 2014-2020 Chris Dickens <christopher.a.dickens@gmail.com>
  * For more information, please visit: http://libusb.info
  *
  * This library is free software; you can redistribute it and/or
 #ifndef LIBUSB_H
 #define LIBUSB_H
 
-#ifdef _MSC_VER
+#if defined(_MSC_VER)
 /* on MS environments, the inline keyword is available in C++ only */
 #if !defined(__cplusplus)
 #define inline __inline
 #endif
-/* ssize_t is also not available (copy/paste from MinGW) */
-#ifndef _SSIZE_T_DEFINED
-#define _SSIZE_T_DEFINED
-#undef ssize_t
-#ifdef _WIN64
-  typedef __int64 ssize_t;
-#else
-  typedef int ssize_t;
-#endif /* _WIN64 */
-#endif /* _SSIZE_T_DEFINED */
+/* ssize_t is also not available */
+#include <basetsd.h>
+typedef SSIZE_T ssize_t;
 #endif /* _MSC_VER */
 
-/* stdint.h is not available on older MSVC */
-#if defined(_MSC_VER) && (_MSC_VER < 1600) && (!defined(_STDINT)) && (!defined(_STDINT_H))
-typedef unsigned __int8   uint8_t;
-typedef unsigned __int16  uint16_t;
-typedef unsigned __int32  uint32_t;
-#else
+#include <limits.h>
 #include <stdint.h>
-#endif
-
-#if !defined(_WIN32_WCE)
 #include <sys/types.h>
-#endif
-
-#if defined(__linux__) || defined(__APPLE__) || defined(__CYGWIN__) || defined(__HAIKU__)
+#if !defined(_MSC_VER)
 #include <sys/time.h>
 #endif
-
 #include <time.h>
-#include <limits.h>
 
 #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
 #define ZERO_SIZED_ARRAY               /* [] - valid C99 code */
 #else
 #define ZERO_SIZED_ARRAY       0       /* [0] - non-standard, but usually working code */
-#endif
+#endif /* __STDC_VERSION__ */
 
 /* 'interface' might be defined as a macro on Windows, so we need to
  * undefine it so as not to break the current libusb API, because
  * libusb_config_descriptor has an 'interface' member
  * As this can be problematic if you include windows.h after libusb.h
  * in your sources, we force windows.h to be included first. */
-#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
+#if defined(_WIN32) || defined(__CYGWIN__)
 #include <windows.h>
 #if defined(interface)
 #undef interface
@@ -80,17 +62,22 @@ typedef unsigned __int32  uint32_t;
 #if !defined(__CYGWIN__)
 #include <winsock.h>
 #endif
-#endif
+#endif /* _WIN32 || __CYGWIN__ */
 
-#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
-#define LIBUSB_DEPRECATED_FOR(f) \
-  __attribute__((deprecated("Use " #f " instead")))
-#elif __GNUC__ >= 3
-#define LIBUSB_DEPRECATED_FOR(f) __attribute__((deprecated))
+#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
+#define LIBUSB_DEPRECATED_FOR(f) __attribute__ ((deprecated ("Use " #f " instead")))
+#elif defined(__GNUC__) && (__GNUC__ >= 3)
+#define LIBUSB_DEPRECATED_FOR(f) __attribute__ ((deprecated))
 #else
 #define LIBUSB_DEPRECATED_FOR(f)
 #endif /* __GNUC__ */
 
+#if defined(__GNUC__)
+#define LIBUSB_PACKED __attribute__ ((packed))
+#else
+#define LIBUSB_PACKED
+#endif /* __GNUC__ */
+
 /** \def LIBUSB_CALL
  * \ingroup libusb_misc
  * libusb's Windows calling convention.
@@ -123,11 +110,11 @@ typedef unsigned __int32  uint32_t;
  * return type, before the function name. See internal documentation for
  * API_EXPORTED.
  */
-#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
+#if defined(_WIN32) || defined(__CYGWIN__)
 #define LIBUSB_CALL WINAPI
 #else
 #define LIBUSB_CALL
-#endif
+#endif /* _WIN32 || __CYGWIN__ */
 
 /** \def LIBUSB_API_VERSION
  * \ingroup libusb_misc
@@ -149,12 +136,12 @@ typedef unsigned __int32  uint32_t;
  * Internally, LIBUSB_API_VERSION is defined as follows:
  * (libusb major << 24) | (libusb minor << 16) | (16 bit incremental)
  */
-#define LIBUSB_API_VERSION 0x01000106
+#define LIBUSB_API_VERSION 0x01000108
 
 /* The following is kept for compatibility, but will be deprecated in the future */
 #define LIBUSBX_API_VERSION LIBUSB_API_VERSION
 
-#ifdef __cplusplus
+#if defined(__cplusplus)
 extern "C" {
 #endif
 
@@ -196,35 +183,35 @@ enum libusb_class_code {
         * this bDeviceClass value indicates that each interface specifies its
         * own class information and all interfaces operate independently.
         */
-       LIBUSB_CLASS_PER_INTERFACE = 0,
+       LIBUSB_CLASS_PER_INTERFACE = 0x00,
 
        /** Audio class */
-       LIBUSB_CLASS_AUDIO = 1,
+       LIBUSB_CLASS_AUDIO = 0x01,
 
        /** Communications class */
-       LIBUSB_CLASS_COMM = 2,
+       LIBUSB_CLASS_COMM = 0x02,
 
        /** Human Interface Device class */
-       LIBUSB_CLASS_HID = 3,
+       LIBUSB_CLASS_HID = 0x03,
 
        /** Physical */
-       LIBUSB_CLASS_PHYSICAL = 5,
-
-       /** Printer class */
-       LIBUSB_CLASS_PRINTER = 7,
+       LIBUSB_CLASS_PHYSICAL = 0x05,
 
        /** Image class */
-       LIBUSB_CLASS_PTP = 6, /* legacy name from libusb-0.1 usb.h */
-       LIBUSB_CLASS_IMAGE = 6,
+       LIBUSB_CLASS_IMAGE = 0x06,
+       LIBUSB_CLASS_PTP = 0x06, /* legacy name from libusb-0.1 usb.h */
+
+       /** Printer class */
+       LIBUSB_CLASS_PRINTER = 0x07,
 
        /** Mass storage class */
-       LIBUSB_CLASS_MASS_STORAGE = 8,
+       LIBUSB_CLASS_MASS_STORAGE = 0x08,
 
        /** Hub class */
-       LIBUSB_CLASS_HUB = 9,
+       LIBUSB_CLASS_HUB = 0x09,
 
        /** Data class */
-       LIBUSB_CLASS_DATA = 10,
+       LIBUSB_CLASS_DATA = 0x0a,
 
        /** Smart Card */
        LIBUSB_CLASS_SMART_CARD = 0x0b,
@@ -244,6 +231,9 @@ enum libusb_class_code {
        /** Wireless class */
        LIBUSB_CLASS_WIRELESS = 0xe0,
 
+       /** Miscellaneous class */
+       LIBUSB_CLASS_MISCELLANEOUS = 0xef,
+
        /** Application class */
        LIBUSB_CLASS_APPLICATION = 0xfe,
 
@@ -311,12 +301,13 @@ enum libusb_descriptor_type {
 #define LIBUSB_BT_CONTAINER_ID_SIZE            20
 
 /* We unwrap the BOS => define its max size */
-#define LIBUSB_DT_BOS_MAX_SIZE         ((LIBUSB_DT_BOS_SIZE)     +\
-                                       (LIBUSB_BT_USB_2_0_EXTENSION_SIZE)       +\
-                                       (LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) +\
-                                       (LIBUSB_BT_CONTAINER_ID_SIZE))
+#define LIBUSB_DT_BOS_MAX_SIZE                         \
+       (LIBUSB_DT_BOS_SIZE +                           \
+        LIBUSB_BT_USB_2_0_EXTENSION_SIZE +             \
+        LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE +      \
+        LIBUSB_BT_CONTAINER_ID_SIZE)
 
-#define LIBUSB_ENDPOINT_ADDRESS_MASK   0x0f    /* in bEndpointAddress */
+#define LIBUSB_ENDPOINT_ADDRESS_MASK           0x0f    /* in bEndpointAddress */
 #define LIBUSB_ENDPOINT_DIR_MASK               0x80
 
 /** \ingroup libusb_desc
@@ -324,34 +315,31 @@ enum libusb_descriptor_type {
  * \ref libusb_endpoint_descriptor::bEndpointAddress "endpoint address" scheme.
  */
 enum libusb_endpoint_direction {
-       /** In: device-to-host */
-       LIBUSB_ENDPOINT_IN = 0x80,
-
        /** Out: host-to-device */
-       LIBUSB_ENDPOINT_OUT = 0x00
+       LIBUSB_ENDPOINT_OUT = 0x00,
+
+       /** In: device-to-host */
+       LIBUSB_ENDPOINT_IN = 0x80
 };
 
-#define LIBUSB_TRANSFER_TYPE_MASK                      0x03    /* in bmAttributes */
+#define LIBUSB_TRANSFER_TYPE_MASK              0x03    /* in bmAttributes */
 
 /** \ingroup libusb_desc
  * Endpoint transfer type. Values for bits 0:1 of the
  * \ref libusb_endpoint_descriptor::bmAttributes "endpoint attributes" field.
  */
-enum libusb_transfer_type {
+enum libusb_endpoint_transfer_type {
        /** Control endpoint */
-       LIBUSB_TRANSFER_TYPE_CONTROL = 0,
+       LIBUSB_ENDPOINT_TRANSFER_TYPE_CONTROL = 0x0,
 
        /** Isochronous endpoint */
-       LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1,
+       LIBUSB_ENDPOINT_TRANSFER_TYPE_ISOCHRONOUS = 0x1,
 
        /** Bulk endpoint */
-       LIBUSB_TRANSFER_TYPE_BULK = 2,
+       LIBUSB_ENDPOINT_TRANSFER_TYPE_BULK = 0x2,
 
        /** Interrupt endpoint */
-       LIBUSB_TRANSFER_TYPE_INTERRUPT = 3,
-
-       /** Stream endpoint */
-       LIBUSB_TRANSFER_TYPE_BULK_STREAM = 4,
+       LIBUSB_ENDPOINT_TRANSFER_TYPE_INTERRUPT = 0x3
 };
 
 /** \ingroup libusb_misc
@@ -386,20 +374,20 @@ enum libusb_standard_request {
        LIBUSB_REQUEST_SET_CONFIGURATION = 0x09,
 
        /** Return the selected alternate setting for the specified interface */
-       LIBUSB_REQUEST_GET_INTERFACE = 0x0A,
+       LIBUSB_REQUEST_GET_INTERFACE = 0x0a,
 
        /** Select an alternate interface for the specified interface */
-       LIBUSB_REQUEST_SET_INTERFACE = 0x0B,
+       LIBUSB_REQUEST_SET_INTERFACE = 0x0b,
 
        /** Set then report an endpoint's synchronization frame */
-       LIBUSB_REQUEST_SYNCH_FRAME = 0x0C,
+       LIBUSB_REQUEST_SYNCH_FRAME = 0x0c,
 
        /** Sets both the U1 and U2 Exit Latency */
        LIBUSB_REQUEST_SET_SEL = 0x30,
 
        /** Delay from the time a host transmits a packet to the time it is
          * received by the device. */
-       LIBUSB_SET_ISOCH_DELAY = 0x31,
+       LIBUSB_SET_ISOCH_DELAY = 0x31
 };
 
 /** \ingroup libusb_misc
@@ -435,10 +423,10 @@ enum libusb_request_recipient {
        LIBUSB_RECIPIENT_ENDPOINT = 0x02,
 
        /** Other */
-       LIBUSB_RECIPIENT_OTHER = 0x03,
+       LIBUSB_RECIPIENT_OTHER = 0x03
 };
 
-#define LIBUSB_ISO_SYNC_TYPE_MASK              0x0C
+#define LIBUSB_ISO_SYNC_TYPE_MASK      0x0c
 
 /** \ingroup libusb_desc
  * Synchronization type for isochronous endpoints. Values for bits 2:3 of the
@@ -447,19 +435,19 @@ enum libusb_request_recipient {
  */
 enum libusb_iso_sync_type {
        /** No synchronization */
-       LIBUSB_ISO_SYNC_TYPE_NONE = 0,
+       LIBUSB_ISO_SYNC_TYPE_NONE = 0x0,
 
        /** Asynchronous */
-       LIBUSB_ISO_SYNC_TYPE_ASYNC = 1,
+       LIBUSB_ISO_SYNC_TYPE_ASYNC = 0x1,
 
        /** Adaptive */
-       LIBUSB_ISO_SYNC_TYPE_ADAPTIVE = 2,
+       LIBUSB_ISO_SYNC_TYPE_ADAPTIVE = 0x2,
 
        /** Synchronous */
-       LIBUSB_ISO_SYNC_TYPE_SYNC = 3
+       LIBUSB_ISO_SYNC_TYPE_SYNC = 0x3
 };
 
-#define LIBUSB_ISO_USAGE_TYPE_MASK 0x30
+#define LIBUSB_ISO_USAGE_TYPE_MASK     0x30
 
 /** \ingroup libusb_desc
  * Usage type for isochronous endpoints. Values for bits 4:5 of the
@@ -468,13 +456,68 @@ enum libusb_iso_sync_type {
  */
 enum libusb_iso_usage_type {
        /** Data endpoint */
-       LIBUSB_ISO_USAGE_TYPE_DATA = 0,
+       LIBUSB_ISO_USAGE_TYPE_DATA = 0x0,
 
        /** Feedback endpoint */
-       LIBUSB_ISO_USAGE_TYPE_FEEDBACK = 1,
+       LIBUSB_ISO_USAGE_TYPE_FEEDBACK = 0x1,
 
        /** Implicit feedback Data endpoint */
-       LIBUSB_ISO_USAGE_TYPE_IMPLICIT = 2,
+       LIBUSB_ISO_USAGE_TYPE_IMPLICIT = 0x2
+};
+
+/** \ingroup libusb_desc
+ * Supported speeds (wSpeedSupported) bitfield. Indicates what
+ * speeds the device supports.
+ */
+enum libusb_supported_speed {
+       /** Low speed operation supported (1.5MBit/s). */
+       LIBUSB_LOW_SPEED_OPERATION = (1 << 0),
+
+       /** Full speed operation supported (12MBit/s). */
+       LIBUSB_FULL_SPEED_OPERATION = (1 << 1),
+
+       /** High speed operation supported (480MBit/s). */
+       LIBUSB_HIGH_SPEED_OPERATION = (1 << 2),
+
+       /** Superspeed operation supported (5000MBit/s). */
+       LIBUSB_SUPER_SPEED_OPERATION = (1 << 3)
+};
+
+/** \ingroup libusb_desc
+ * Masks for the bits of the
+ * \ref libusb_usb_2_0_extension_descriptor::bmAttributes "bmAttributes" field
+ * of the USB 2.0 Extension descriptor.
+ */
+enum libusb_usb_2_0_extension_attributes {
+       /** Supports Link Power Management (LPM) */
+       LIBUSB_BM_LPM_SUPPORT = (1 << 1)
+};
+
+/** \ingroup libusb_desc
+ * Masks for the bits of the
+ * \ref libusb_ss_usb_device_capability_descriptor::bmAttributes "bmAttributes" field
+ * field of the SuperSpeed USB Device Capability descriptor.
+ */
+enum libusb_ss_usb_device_capability_attributes {
+       /** Supports Latency Tolerance Messages (LTM) */
+       LIBUSB_BM_LTM_SUPPORT = (1 << 1)
+};
+
+/** \ingroup libusb_desc
+ * USB capability types
+ */
+enum libusb_bos_type {
+       /** Wireless USB device capability */
+       LIBUSB_BT_WIRELESS_USB_DEVICE_CAPABILITY = 0x01,
+
+       /** USB 2.0 extensions */
+       LIBUSB_BT_USB_2_0_EXTENSION = 0x02,
+
+       /** SuperSpeed USB device capability */
+       LIBUSB_BT_SS_USB_DEVICE_CAPABILITY = 0x03,
+
+       /** Container ID type */
+       LIBUSB_BT_CONTAINER_ID = 0x04
 };
 
 /** \ingroup libusb_desc
@@ -547,17 +590,15 @@ struct libusb_endpoint_descriptor {
 
        /** The address of the endpoint described by this descriptor. Bits 0:3 are
         * the endpoint number. Bits 4:6 are reserved. Bit 7 indicates direction,
-        * see \ref libusb_endpoint_direction.
-        */
+        * see \ref libusb_endpoint_direction. */
        uint8_t  bEndpointAddress;
 
        /** Attributes which apply to the endpoint when it is configured using
         * the bConfigurationValue. Bits 0:1 determine the transfer type and
-        * correspond to \ref libusb_transfer_type. Bits 2:3 are only used for
-        * isochronous endpoints and correspond to \ref libusb_iso_sync_type.
+        * correspond to \ref libusb_endpoint_transfer_type. Bits 2:3 are only used
+        * for isochronous endpoints and correspond to \ref libusb_iso_sync_type.
         * Bits 4:5 are also only used for isochronous endpoints and correspond to
-        * \ref libusb_iso_usage_type. Bits 6:7 are reserved.
-        */
+        * \ref libusb_iso_usage_type. Bits 6:7 are reserved. */
        uint8_t  bmAttributes;
 
        /** Maximum packet size this endpoint is capable of sending/receiving. */
@@ -577,7 +618,7 @@ struct libusb_endpoint_descriptor {
         * it will store them here, should you wish to parse them. */
        const unsigned char *extra;
 
-       /** Length of the extra descriptors, in bytes. */
+       /** Length of the extra descriptors, in bytes. Must be non-negative. */
        int extra_length;
 };
 
@@ -627,7 +668,7 @@ struct libusb_interface_descriptor {
         * it will store them here, should you wish to parse them. */
        const unsigned char *extra;
 
-       /** Length of the extra descriptors, in bytes. */
+       /** Length of the extra descriptors, in bytes. Must be non-negative. */
        int extra_length;
 };
 
@@ -639,7 +680,8 @@ struct libusb_interface {
         * by the num_altsetting field. */
        const struct libusb_interface_descriptor *altsetting;
 
-       /** The number of alternate settings that belong to this interface */
+       /** The number of alternate settings that belong to this interface.
+        * Must be non-negative. */
        int num_altsetting;
 };
 
@@ -686,7 +728,7 @@ struct libusb_config_descriptor {
         * descriptors, it will store them here, should you wish to parse them. */
        const unsigned char *extra;
 
-       /** Length of the extra descriptors, in bytes. */
+       /** Length of the extra descriptors, in bytes. Must be non-negative. */
        int extra_length;
 };
 
@@ -697,7 +739,6 @@ struct libusb_config_descriptor {
  * host-endian format.
  */
 struct libusb_ss_endpoint_companion_descriptor {
-
        /** Size of this descriptor (in bytes) */
        uint8_t  bLength;
 
@@ -706,19 +747,18 @@ struct libusb_ss_endpoint_companion_descriptor {
         * this context. */
        uint8_t  bDescriptorType;
 
-
        /** The maximum number of packets the endpoint can send or
         *  receive as part of a burst. */
        uint8_t  bMaxBurst;
 
-       /** In bulk EP: bits 4:0 represents the maximum number of
-        *  streams the EP supports. In isochronous EP: bits 1:0
-        *  represents the Mult - a zero based value that determines
-        *  the maximum number of packets within a service interval  */
+       /** In bulk EP: bits 4:0 represents the maximum number of
+        *  streams the EP supports. In isochronous EP: bits 1:0
+        *  represents the Mult - a zero based value that determines
+        *  the maximum number of packets within a service interval  */
        uint8_t  bmAttributes;
 
-       /** The total number of bytes this EP will transfer every
-        *  service interval. valid only for periodic EPs. */
+       /** The total number of bytes this EP will transfer every
+        *  service interval. Valid only for periodic EPs. */
        uint16_t wBytesPerInterval;
 };
 
@@ -729,15 +769,18 @@ struct libusb_ss_endpoint_companion_descriptor {
  */
 struct libusb_bos_dev_capability_descriptor {
        /** Size of this descriptor (in bytes) */
-       uint8_t bLength;
+       uint8_t  bLength;
+
        /** Descriptor type. Will have value
         * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY
         * LIBUSB_DT_DEVICE_CAPABILITY in this context. */
-       uint8_t bDescriptorType;
+       uint8_t  bDescriptorType;
+
        /** Device Capability type */
-       uint8_t bDevCapabilityType;
+       uint8_t  bDevCapabilityType;
+
        /** Device Capability data (bLength - 3 bytes) */
-       uint8_t dev_capability_data[ZERO_SIZED_ARRAY];
+       uint8_t  dev_capability_data[ZERO_SIZED_ARRAY];
 };
 
 /** \ingroup libusb_desc
@@ -788,7 +831,7 @@ struct libusb_usb_2_0_extension_descriptor {
         * A value of one in a bit location indicates a feature is
         * supported; a value of zero indicates it is not supported.
         * See \ref libusb_usb_2_0_extension_attributes. */
-       uint32_t  bmAttributes;
+       uint32_t bmAttributes;
 };
 
 /** \ingroup libusb_desc
@@ -853,7 +896,7 @@ struct libusb_container_id_descriptor {
        uint8_t  bDevCapabilityType;
 
        /** Reserved field */
-       uint8_t bReserved;
+       uint8_t  bReserved;
 
        /** 128 bit UUID */
        uint8_t  ContainerID[16];
@@ -861,6 +904,9 @@ struct libusb_container_id_descriptor {
 
 /** \ingroup libusb_asyncio
  * Setup packet for control transfers. */
+#if defined(_MSC_VER)
+#pragma pack(push, 1)
+#endif
 struct libusb_control_setup {
        /** Request type. Bits 0:4 determine recipient, see
         * \ref libusb_request_recipient. Bits 5:6 determine type, see
@@ -885,7 +931,10 @@ struct libusb_control_setup {
 
        /** Number of bytes to transfer */
        uint16_t wLength;
-};
+} LIBUSB_PACKED;
+#if defined(_MSC_VER)
+#pragma pack(pop)
+#endif
 
 #define LIBUSB_CONTROL_SETUP_SIZE (sizeof(struct libusb_control_setup))
 
@@ -915,7 +964,7 @@ struct libusb_version {
        const char *rc;
 
        /** For ABI compatibility only. */
-       const chardescribe;
+       const char *describe;
 };
 
 /** \ingroup libusb_lib
@@ -985,62 +1034,7 @@ enum libusb_speed {
        LIBUSB_SPEED_SUPER = 4,
 
        /** The device is operating at super speed plus (10000MBit/s). */
-       LIBUSB_SPEED_SUPER_PLUS = 5,
-};
-
-/** \ingroup libusb_dev
- * Supported speeds (wSpeedSupported) bitfield. Indicates what
- * speeds the device supports.
- */
-enum libusb_supported_speed {
-       /** Low speed operation supported (1.5MBit/s). */
-       LIBUSB_LOW_SPEED_OPERATION   = 1,
-
-       /** Full speed operation supported (12MBit/s). */
-       LIBUSB_FULL_SPEED_OPERATION  = 2,
-
-       /** High speed operation supported (480MBit/s). */
-       LIBUSB_HIGH_SPEED_OPERATION  = 4,
-
-       /** Superspeed operation supported (5000MBit/s). */
-       LIBUSB_SUPER_SPEED_OPERATION = 8,
-};
-
-/** \ingroup libusb_dev
- * Masks for the bits of the
- * \ref libusb_usb_2_0_extension_descriptor::bmAttributes "bmAttributes" field
- * of the USB 2.0 Extension descriptor.
- */
-enum libusb_usb_2_0_extension_attributes {
-       /** Supports Link Power Management (LPM) */
-       LIBUSB_BM_LPM_SUPPORT = 2,
-};
-
-/** \ingroup libusb_dev
- * Masks for the bits of the
- * \ref libusb_ss_usb_device_capability_descriptor::bmAttributes "bmAttributes" field
- * field of the SuperSpeed USB Device Capability descriptor.
- */
-enum libusb_ss_usb_device_capability_attributes {
-       /** Supports Latency Tolerance Messages (LTM) */
-       LIBUSB_BM_LTM_SUPPORT = 2,
-};
-
-/** \ingroup libusb_dev
- * USB capability types
- */
-enum libusb_bos_type {
-       /** Wireless USB device capability */
-       LIBUSB_BT_WIRELESS_USB_DEVICE_CAPABILITY        = 1,
-
-       /** USB 2.0 extensions */
-       LIBUSB_BT_USB_2_0_EXTENSION                     = 2,
-
-       /** SuperSpeed USB device capability */
-       LIBUSB_BT_SS_USB_DEVICE_CAPABILITY              = 3,
-
-       /** Container ID type */
-       LIBUSB_BT_CONTAINER_ID                          = 4,
+       LIBUSB_SPEED_SUPER_PLUS = 5
 };
 
 /** \ingroup libusb_misc
@@ -1094,12 +1088,31 @@ enum libusb_error {
           message strings in strerror.c when adding new error codes here. */
 
        /** Other error */
-       LIBUSB_ERROR_OTHER = -99,
+       LIBUSB_ERROR_OTHER = -99
 };
 
 /* Total number of error codes in enum libusb_error */
 #define LIBUSB_ERROR_COUNT 14
 
+/** \ingroup libusb_asyncio
+ * Transfer type */
+enum libusb_transfer_type {
+       /** Control transfer */
+       LIBUSB_TRANSFER_TYPE_CONTROL = 0U,
+
+       /** Isochronous transfer */
+       LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1U,
+
+       /** Bulk transfer */
+       LIBUSB_TRANSFER_TYPE_BULK = 2U,
+
+       /** Interrupt transfer */
+       LIBUSB_TRANSFER_TYPE_INTERRUPT = 3U,
+
+       /** Bulk stream transfer */
+       LIBUSB_TRANSFER_TYPE_BULK_STREAM = 4U
+};
+
 /** \ingroup libusb_asyncio
  * Transfer status codes */
 enum libusb_transfer_status {
@@ -1124,7 +1137,7 @@ enum libusb_transfer_status {
        LIBUSB_TRANSFER_NO_DEVICE,
 
        /** Device sent more data than requested */
-       LIBUSB_TRANSFER_OVERFLOW,
+       LIBUSB_TRANSFER_OVERFLOW
 
        /* NB! Remember to update libusb_error_name()
           when adding new status codes here. */
@@ -1134,19 +1147,19 @@ enum libusb_transfer_status {
  * libusb_transfer.flags values */
 enum libusb_transfer_flags {
        /** Report short frames as errors */
-       LIBUSB_TRANSFER_SHORT_NOT_OK = 1<<0,
+       LIBUSB_TRANSFER_SHORT_NOT_OK = (1U << 0),
 
        /** Automatically free() transfer buffer during libusb_free_transfer().
         * Note that buffers allocated with libusb_dev_mem_alloc() should not
         * be attempted freed in this way, since free() is not an appropriate
         * way to release such memory. */
-       LIBUSB_TRANSFER_FREE_BUFFER = 1<<1,
+       LIBUSB_TRANSFER_FREE_BUFFER = (1U << 1),
 
        /** Automatically call libusb_free_transfer() after callback returns.
         * If this flag is set, it is illegal to call libusb_free_transfer()
         * from your transfer callback, as this will result in a double-free
         * when this flag is acted upon. */
-       LIBUSB_TRANSFER_FREE_TRANSFER = 1<<2,
+       LIBUSB_TRANSFER_FREE_TRANSFER = (1U << 2),
 
        /** Terminate transfers that are a multiple of the endpoint's
         * wMaxPacketSize with an extra zero length packet. This is useful
@@ -1171,7 +1184,7 @@ enum libusb_transfer_flags {
         *
         * Available since libusb-1.0.9.
         */
-       LIBUSB_TRANSFER_ADD_ZERO_PACKET = 1 << 3,
+       LIBUSB_TRANSFER_ADD_ZERO_PACKET = (1U << 3)
 };
 
 /** \ingroup libusb_asyncio
@@ -1216,7 +1229,7 @@ struct libusb_transfer {
        /** Address of the endpoint where this transfer will be sent. */
        unsigned char endpoint;
 
-       /** Type of the endpoint from \ref libusb_transfer_type */
+       /** Type of the transfer from \ref libusb_transfer_type */
        unsigned char type;
 
        /** Timeout for this transfer in milliseconds. A value of 0 indicates no
@@ -1232,7 +1245,7 @@ struct libusb_transfer {
         * to determine if errors occurred. */
        enum libusb_transfer_status status;
 
-       /** Length of the data buffer */
+       /** Length of the data buffer. Must be non-negative. */
        int length;
 
        /** Actual length of data that was transferred. Read-only, and only for
@@ -1244,14 +1257,23 @@ struct libusb_transfer {
         * fails, or is cancelled. */
        libusb_transfer_cb_fn callback;
 
-       /** User context data to pass to the callback function. */
+       /** User context data. Useful for associating specific data to a transfer
+        * that can be accessed from within the callback function.
+        *
+        * This field may be set manually or is taken as the `user_data` parameter
+        * of the following functions:
+        * - libusb_fill_bulk_transfer()
+        * - libusb_fill_bulk_stream_transfer()
+        * - libusb_fill_control_transfer()
+        * - libusb_fill_interrupt_transfer()
+        * - libusb_fill_iso_transfer() */
        void *user_data;
 
        /** Data buffer */
        unsigned char *buffer;
 
        /** Number of isochronous packets. Only used for I/O with isochronous
-        * endpoints. */
+        * endpoints. Must be non-negative. */
        int num_iso_packets;
 
        /** Isochronous packet descriptors, for isochronous transfers only. */
@@ -1265,44 +1287,75 @@ struct libusb_transfer {
  */
 enum libusb_capability {
        /** The libusb_has_capability() API is available. */
-       LIBUSB_CAP_HAS_CAPABILITY = 0x0000,
+       LIBUSB_CAP_HAS_CAPABILITY = 0x0000U,
+
        /** Hotplug support is available on this platform. */
-       LIBUSB_CAP_HAS_HOTPLUG = 0x0001,
+       LIBUSB_CAP_HAS_HOTPLUG = 0x0001U,
+
        /** The library can access HID devices without requiring user intervention.
         * Note that before being able to actually access an HID device, you may
         * still have to call additional libusb functions such as
         * \ref libusb_detach_kernel_driver(). */
-       LIBUSB_CAP_HAS_HID_ACCESS = 0x0100,
-       /** The library supports detaching of the default USB driver, using 
+       LIBUSB_CAP_HAS_HID_ACCESS = 0x0100U,
+
+       /** The library supports detaching of the default USB driver, using
         * \ref libusb_detach_kernel_driver(), if one is set by the OS kernel */
-       LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER = 0x0101
+       LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER = 0x0101U
 };
 
 /** \ingroup libusb_lib
  *  Log message levels.
- *  - LIBUSB_LOG_LEVEL_NONE (0)    : no messages ever printed by the library (default)
- *  - LIBUSB_LOG_LEVEL_ERROR (1)   : error messages are printed to stderr
- *  - LIBUSB_LOG_LEVEL_WARNING (2) : warning and error messages are printed to stderr
- *  - LIBUSB_LOG_LEVEL_INFO (3)    : informational messages are printed to stderr
- *  - LIBUSB_LOG_LEVEL_DEBUG (4)   : debug and informational messages are printed to stderr
  */
 enum libusb_log_level {
+       /** (0) : No messages ever emitted by the library (default) */
        LIBUSB_LOG_LEVEL_NONE = 0,
+
+       /** (1) : Error messages are emitted */
        LIBUSB_LOG_LEVEL_ERROR = 1,
+
+       /** (2) : Warning and error messages are emitted */
        LIBUSB_LOG_LEVEL_WARNING = 2,
+
+       /** (3) : Informational, warning and error messages are emitted */
        LIBUSB_LOG_LEVEL_INFO = 3,
-       LIBUSB_LOG_LEVEL_DEBUG = 4,
+
+       /** (4) : All messages are emitted */
+       LIBUSB_LOG_LEVEL_DEBUG = 4
 };
 
+/** \ingroup libusb_lib
+ *  Log callback mode.
+ * \see libusb_set_log_cb()
+ */
+enum libusb_log_cb_mode {
+       /** Callback function handling all log messages. */
+       LIBUSB_LOG_CB_GLOBAL = (1 << 0),
+
+       /** Callback function handling context related log messages. */
+       LIBUSB_LOG_CB_CONTEXT = (1 << 1)
+};
+
+/** \ingroup libusb_lib
+ * Callback function for handling log messages.
+ * \param ctx the context which is related to the log message, or NULL if it
+ * is a global log message
+ * \param level the log level, see \ref libusb_log_level for a description
+ * \param str the log message
+ * \see libusb_set_log_cb()
+ */
+typedef void (LIBUSB_CALL *libusb_log_cb)(libusb_context *ctx,
+       enum libusb_log_level level, const char *str);
+
 int LIBUSB_CALL libusb_init(libusb_context **ctx);
 void LIBUSB_CALL libusb_exit(libusb_context *ctx);
 LIBUSB_DEPRECATED_FOR(libusb_set_option)
 void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level);
+void LIBUSB_CALL libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb, int mode);
 const struct libusb_version * LIBUSB_CALL libusb_get_version(void);
 int LIBUSB_CALL libusb_has_capability(uint32_t capability);
 const char * LIBUSB_CALL libusb_error_name(int errcode);
 int LIBUSB_CALL libusb_setlocale(const char *locale);
-const char * LIBUSB_CALL libusb_strerror(enum libusb_error errcode);
+const char * LIBUSB_CALL libusb_strerror(int errcode);
 
 ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context *ctx,
        libusb_device ***list);
@@ -1324,7 +1377,7 @@ int LIBUSB_CALL libusb_get_config_descriptor_by_value(libusb_device *dev,
 void LIBUSB_CALL libusb_free_config_descriptor(
        struct libusb_config_descriptor *config);
 int LIBUSB_CALL libusb_get_ss_endpoint_companion_descriptor(
-       struct libusb_context *ctx,
+       libusb_context *ctx,
        const struct libusb_endpoint_descriptor *endpoint,
        struct libusb_ss_endpoint_companion_descriptor **ep_comp);
 void LIBUSB_CALL libusb_free_ss_endpoint_companion_descriptor(
@@ -1333,27 +1386,27 @@ int LIBUSB_CALL libusb_get_bos_descriptor(libusb_device_handle *dev_handle,
        struct libusb_bos_descriptor **bos);
 void LIBUSB_CALL libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos);
 int LIBUSB_CALL libusb_get_usb_2_0_extension_descriptor(
-       struct libusb_context *ctx,
+       libusb_context *ctx,
        struct libusb_bos_dev_capability_descriptor *dev_cap,
        struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension);
 void LIBUSB_CALL libusb_free_usb_2_0_extension_descriptor(
        struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension);
 int LIBUSB_CALL libusb_get_ss_usb_device_capability_descriptor(
-       struct libusb_context *ctx,
+       libusb_context *ctx,
        struct libusb_bos_dev_capability_descriptor *dev_cap,
        struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_cap);
 void LIBUSB_CALL libusb_free_ss_usb_device_capability_descriptor(
        struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap);
-int LIBUSB_CALL libusb_get_container_id_descriptor(struct libusb_context *ctx,
+int LIBUSB_CALL libusb_get_container_id_descriptor(libusb_context *ctx,
        struct libusb_bos_dev_capability_descriptor *dev_cap,
        struct libusb_container_id_descriptor **container_id);
 void LIBUSB_CALL libusb_free_container_id_descriptor(
        struct libusb_container_id_descriptor *container_id);
 uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev);
 uint8_t LIBUSB_CALL libusb_get_port_number(libusb_device *dev);
-int LIBUSB_CALL libusb_get_port_numbers(libusb_device *dev, uint8_tport_numbers, int port_numbers_len);
+int LIBUSB_CALL libusb_get_port_numbers(libusb_device *dev, uint8_t *port_numbers, int port_numbers_len);
 LIBUSB_DEPRECATED_FOR(libusb_get_port_numbers)
-int LIBUSB_CALL libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_tpath, uint8_t path_length);
+int LIBUSB_CALL libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_t *path, uint8_t path_length);
 libusb_device * LIBUSB_CALL libusb_get_parent(libusb_device *dev);
 uint8_t LIBUSB_CALL libusb_get_device_address(libusb_device *dev);
 int LIBUSB_CALL libusb_get_device_speed(libusb_device *dev);
@@ -1362,6 +1415,7 @@ int LIBUSB_CALL libusb_get_max_packet_size(libusb_device *dev,
 int LIBUSB_CALL libusb_get_max_iso_packet_size(libusb_device *dev,
        unsigned char endpoint);
 
+int LIBUSB_CALL libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev, libusb_device_handle **dev_handle);
 int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **dev_handle);
 void LIBUSB_CALL libusb_close(libusb_device_handle *dev_handle);
 libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle);
@@ -1436,7 +1490,7 @@ static inline unsigned char *libusb_control_transfer_get_data(
 static inline struct libusb_control_setup *libusb_control_transfer_get_setup(
        struct libusb_transfer *transfer)
 {
-       return (struct libusb_control_setup *)(void *) transfer->buffer;
+       return (struct libusb_control_setup *)(void *)transfer->buffer;
 }
 
 /** \ingroup libusb_asyncio
@@ -1466,7 +1520,7 @@ static inline void libusb_fill_control_setup(unsigned char *buffer,
        uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
        uint16_t wLength)
 {
-       struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *) buffer;
+       struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *)buffer;
        setup->bmRequestType = bmRequestType;
        setup->bRequest = bRequest;
        setup->wValue = libusb_cpu_to_le16(wValue);
@@ -1516,7 +1570,7 @@ static inline void libusb_fill_control_transfer(
        unsigned char *buffer, libusb_transfer_cb_fn callback, void *user_data,
        unsigned int timeout)
 {
-       struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *) buffer;
+       struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *)buffer;
        transfer->dev_handle = dev_handle;
        transfer->endpoint = 0;
        transfer->type = LIBUSB_TRANSFER_TYPE_CONTROL;
@@ -1655,6 +1709,7 @@ static inline void libusb_set_iso_packet_lengths(
        struct libusb_transfer *transfer, unsigned int length)
 {
        int i;
+
        for (i = 0; i < transfer->num_iso_packets; i++)
                transfer->iso_packet_desc[i].length = length;
 }
@@ -1869,7 +1924,7 @@ void LIBUSB_CALL libusb_set_pollfd_notifiers(libusb_context *ctx,
  * Callbacks handles are generated by libusb_hotplug_register_callback()
  * and can be used to deregister callbacks. Callback handles are unique
  * per libusb_context and it is safe to call libusb_hotplug_deregister_callback()
- * on an already deregisted callback.
+ * on an already deregistered callback.
  *
  * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
  *
@@ -1881,29 +1936,30 @@ typedef int libusb_hotplug_callback_handle;
  *
  * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
  *
- * Flags for hotplug events */
+ * Hotplug events */
 typedef enum {
-       /** Default value when not using any flags. */
-       LIBUSB_HOTPLUG_NO_FLAGS = 0,
+       /** A device has been plugged in and is ready to use */
+       LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED = (1 << 0),
 
-       /** Arm the callback and fire it for all matching currently attached devices. */
-       LIBUSB_HOTPLUG_ENUMERATE = 1<<0,
-} libusb_hotplug_flag;
+       /** A device has left and is no longer available.
+        * It is the user's responsibility to call libusb_close on any handle associated with a disconnected device.
+        * It is safe to call libusb_get_device_descriptor on a device that has left */
+       LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT = (1 << 1)
+} libusb_hotplug_event;
 
 /** \ingroup libusb_hotplug
  *
  * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
  *
- * Hotplug events */
+ * Hotplug flags */
 typedef enum {
-       /** A device has been plugged in and is ready to use */
-       LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED = 0x01,
+       /** Arm the callback and fire it for all matching currently attached devices. */
+       LIBUSB_HOTPLUG_ENUMERATE = (1 << 0)
+} libusb_hotplug_flag;
 
-       /** A device has left and is no longer available.
-        * It is the user's responsibility to call libusb_close on any handle associated with a disconnected device.
-        * It is safe to call libusb_get_device_descriptor on a device that has left */
-       LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT    = 0x02,
-} libusb_hotplug_event;
+/** \ingroup libusb_hotplug
+ * Convenience macro when not using any flags */
+#define LIBUSB_HOTPLUG_NO_FLAGS 0
 
 /** \ingroup libusb_hotplug
  * Wildcard matching for hotplug events */
@@ -1932,9 +1988,7 @@ typedef enum {
  *                       returning 1 will cause this callback to be deregistered
  */
 typedef int (LIBUSB_CALL *libusb_hotplug_callback_fn)(libusb_context *ctx,
-                                               libusb_device *device,
-                                               libusb_hotplug_event event,
-                                               void *user_data);
+       libusb_device *device, libusb_hotplug_event event, void *user_data);
 
 /** \ingroup libusb_hotplug
  * Register a hotplug callback function
@@ -1959,9 +2013,10 @@ typedef int (LIBUSB_CALL *libusb_hotplug_callback_fn)(libusb_context *ctx,
  * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
  *
  * \param[in] ctx context to register this callback with
- * \param[in] events bitwise or of events that will trigger this callback. See \ref
- *            libusb_hotplug_event
- * \param[in] flags hotplug callback flags. See \ref libusb_hotplug_flag
+ * \param[in] events bitwise or of hotplug events that will trigger this callback.
+ *            See \ref libusb_hotplug_event
+ * \param[in] flags bitwise or of hotplug flags that affect registration.
+ *            See \ref libusb_hotplug_flag
  * \param[in] vendor_id the vendor id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY
  * \param[in] product_id the product id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY
  * \param[in] dev_class the device class to match or \ref LIBUSB_HOTPLUG_MATCH_ANY
@@ -1971,13 +2026,10 @@ typedef int (LIBUSB_CALL *libusb_hotplug_callback_fn)(libusb_context *ctx,
  * \returns LIBUSB_SUCCESS on success LIBUSB_ERROR code on failure
  */
 int LIBUSB_CALL libusb_hotplug_register_callback(libusb_context *ctx,
-                                               libusb_hotplug_event events,
-                                               libusb_hotplug_flag flags,
-                                               int vendor_id, int product_id,
-                                               int dev_class,
-                                               libusb_hotplug_callback_fn cb_fn,
-                                               void *user_data,
-                                               libusb_hotplug_callback_handle *callback_handle);
+       int events, int flags,
+       int vendor_id, int product_id, int dev_class,
+       libusb_hotplug_callback_fn cb_fn, void *user_data,
+       libusb_hotplug_callback_handle *callback_handle);
 
 /** \ingroup libusb_hotplug
  * Deregisters a hotplug callback.
@@ -1991,7 +2043,18 @@ int LIBUSB_CALL libusb_hotplug_register_callback(libusb_context *ctx,
  * \param[in] callback_handle the handle of the callback to deregister
  */
 void LIBUSB_CALL libusb_hotplug_deregister_callback(libusb_context *ctx,
-                                               libusb_hotplug_callback_handle callback_handle);
+       libusb_hotplug_callback_handle callback_handle);
+
+/** \ingroup libusb_hotplug
+ * Gets the user_data associated with a hotplug callback.
+ *
+ * Since version v1.0.24 \ref LIBUSB_API_VERSION >= 0x01000108
+ *
+ * \param[in] ctx context this callback is registered with
+ * \param[in] callback_handle the handle of the callback to get the user_data of
+ */
+void * LIBUSB_CALL libusb_hotplug_get_user_data(libusb_context *ctx,
+       libusb_hotplug_callback_handle callback_handle);
 
 /** \ingroup libusb_lib
  * Available option values for libusb_set_option().
@@ -2018,7 +2081,7 @@ enum libusb_option {
         * If libusb was compiled with verbose debug message logging, this function
         * does nothing: you'll always get messages from all levels.
         */
-       LIBUSB_OPTION_LOG_LEVEL,
+       LIBUSB_OPTION_LOG_LEVEL = 0,
 
        /** Use the UsbDk backend for a specific context, if available.
         *
@@ -2027,12 +2090,23 @@ enum libusb_option {
         *
         * Only valid on Windows.
         */
-       LIBUSB_OPTION_USE_USBDK,
+       LIBUSB_OPTION_USE_USBDK = 1,
+
+       /** Set libusb has weak authority. With this option, libusb will skip
+        * scan devices in libusb_init.
+        *
+        * This option should be set before calling libusb_init(), otherwise
+        * libusb_init will failed. Normally libusb_wrap_sys_device need set
+        * this option.
+        *
+        * Only valid on Linux-based operating system, such as Android.
+        */
+       LIBUSB_OPTION_WEAK_AUTHORITY = 2
 };
 
 int LIBUSB_CALL libusb_set_option(libusb_context *ctx, enum libusb_option option, ...);
 
-#ifdef __cplusplus
+#if defined(__cplusplus)
 }
 #endif
 
index a5d543756c200b25648d3acc3f0b8cb1b9eaaf14..07c7d23dad6b77f61ec7a8ad9b76c72af575ee2c 100644 (file)
@@ -15,7 +15,7 @@ SOURCES = core.c \
   strerror.c \
   sync.c \
   os/darwin_usb.c \
-  os/poll_posix.c \
+  os/events_posix.c \
   os/threads_posix.c
 
 HEADERS = hotplug.h \
@@ -24,7 +24,7 @@ HEADERS = hotplug.h \
   version.h \
   version_nano.h \
   os/darwin_usb.h \
-  os/poll_posix.h \
+  os/events_posix.h \
   os/threads_posix.h
 
 # We use libusb-1.0.0's hardcoded config.h for Xcode
index 31d6ce98d49cdb5c35c6d909e80032e2270514b2..491114be94a391a558f4aeec34a9a28f8c7a9fbd 100644 (file)
@@ -2,6 +2,9 @@
  * Internal header for libusb
  * Copyright © 2007-2009 Daniel Drake <dsd@gentoo.org>
  * Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com>
+ * Copyright © 2019 Nathan Hjelm <hjelmn@cs.umm.edu>
+ * Copyright © 2019-2020 Google LLC. All rights reserved.
+ * Copyright © 2020 Chris Dickens <christopher.a.dickens@gmail.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
 
 #include <config.h>
 
-#include <stdlib.h>
-
-#include <stddef.h>
-#include <stdint.h>
-#include <time.h>
+#include <assert.h>
+#include <inttypes.h>
 #include <stdarg.h>
-#ifdef HAVE_POLL_H
-#include <poll.h>
-#endif
-#ifdef HAVE_MISSING_H
-#include <missing.h>
+#include <stddef.h>
+#include <stdlib.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
 #endif
 
 #include "libusb.h"
-#include "version.h"
 
-/* Attribute to ensure that a structure member is aligned to a natural
- * pointer alignment. Used for os_priv member. */
-#if defined(_MSC_VER)
-#if defined(_WIN64)
-#define PTR_ALIGNED __declspec(align(8))
+/* Not all C standard library headers define static_assert in assert.h
+ * Additionally, Visual Studio treats static_assert as a keyword.
+ */
+#if !defined(__cplusplus) && !defined(static_assert) && !defined(_MSC_VER)
+#define static_assert(cond, msg) _Static_assert(cond, msg)
+#endif
+
+#ifdef NDEBUG
+#define ASSERT_EQ(expression, value)   (void)expression
+#define ASSERT_NE(expression, value)   (void)expression
 #else
-#define PTR_ALIGNED __declspec(align(4))
+#define ASSERT_EQ(expression, value)   assert(expression == value)
+#define ASSERT_NE(expression, value)   assert(expression != value)
+#endif
+
+#define container_of(ptr, type, member) \
+       ((type *)((uintptr_t)(ptr) - (uintptr_t)offsetof(type, member)))
+
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(array) (sizeof(array) / sizeof(array[0]))
 #endif
-#elif defined(__GNUC__)
-#define PTR_ALIGNED __attribute__((aligned(sizeof(void *))))
+
+#ifndef CLAMP
+#define CLAMP(val, min, max) \
+       ((val) < (min) ? (min) : ((val) > (max) ? (max) : (val)))
+#endif
+
+#ifndef MIN
+#define MIN(a, b)      ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef MAX
+#define MAX(a, b)      ((a) > (b) ? (a) : (b))
+#endif
+
+/* The following is used to silence warnings for unused variables */
+#if defined(UNREFERENCED_PARAMETER)
+#define UNUSED(var)    UNREFERENCED_PARAMETER(var)
 #else
-#define PTR_ALIGNED
+#define UNUSED(var)    do { (void)(var); } while(0)
+#endif
+
+/* Macro to align a value up to the next multiple of the size of a pointer */
+#define PTR_ALIGN(v) \
+       (((v) + (sizeof(void *) - 1)) & ~(sizeof(void *) - 1))
+
+/* Internal abstractions for event handling and thread synchronization */
+#if defined(PLATFORM_POSIX)
+#include "os/events_posix.h"
+#include "os/threads_posix.h"
+#elif defined(PLATFORM_WINDOWS)
+#include "os/events_windows.h"
+#include "os/threads_windows.h"
 #endif
 
 /* Inside the libusb code, mark all public functions as follows:
 extern "C" {
 #endif
 
-#define DEVICE_DESC_LENGTH     18
-
 #define USB_MAXENDPOINTS       32
 #define USB_MAXINTERFACES      32
 #define USB_MAXCONFIG          8
@@ -81,13 +118,6 @@ extern "C" {
 /* Terminator for log lines */
 #define USBI_LOG_LINE_END      "\n"
 
-/* The following is used to silence warnings for unused variables */
-#define UNUSED(var)            do { (void)(var); } while(0)
-
-#if !defined(ARRAYSIZE)
-#define ARRAYSIZE(array) (sizeof(array) / sizeof(array[0]))
-#endif
-
 struct list_head {
        struct list_head *prev, *next;
 };
@@ -98,11 +128,14 @@ struct list_head {
  *  member - the list_head element in "type"
  */
 #define list_entry(ptr, type, member) \
-       ((type *)((uintptr_t)(ptr) - (uintptr_t)offsetof(type, member)))
+       container_of(ptr, type, member)
 
 #define list_first_entry(ptr, type, member) \
        list_entry((ptr)->next, type, member)
 
+#define list_next_entry(ptr, type, member) \
+       list_entry((ptr)->member.next, type, member)
+
 /* Get each entry from a list
  *  pos - A structure pointer has a "member" element
  *  head - list head
@@ -110,15 +143,24 @@ struct list_head {
  *  type - the type of the first parameter
  */
 #define list_for_each_entry(pos, head, member, type)                   \
-       for (pos = list_entry((head)->next, type, member);              \
+       for (pos = list_first_entry(head, type, member);                \
                 &pos->member != (head);                                \
-                pos = list_entry(pos->member.next, type, member))
+                pos = list_next_entry(pos, type, member))
 
 #define list_for_each_entry_safe(pos, n, head, member, type)           \
-       for (pos = list_entry((head)->next, type, member),              \
-                n = list_entry(pos->member.next, type, member);        \
+       for (pos = list_first_entry(head, type, member),                \
+                n = list_next_entry(pos, type, member);                \
                 &pos->member != (head);                                \
-                pos = n, n = list_entry(n->member.next, type, member))
+                pos = n, n = list_next_entry(n, type, member))
+
+/* Helper macros to iterate over a list. The structure pointed
+ * to by "pos" must have a list_head member named "list".
+ */
+#define for_each_helper(pos, head, type) \
+       list_for_each_entry(pos, head, list, type)
+
+#define for_each_safe_helper(pos, n, head, type) \
+       list_for_each_entry_safe(pos, n, head, list, type)
 
 #define list_empty(entry) ((entry)->next == (entry))
 
@@ -155,8 +197,10 @@ static inline void list_del(struct list_head *entry)
 
 static inline void list_cut(struct list_head *list, struct list_head *head)
 {
-       if (list_empty(head))
+       if (list_empty(head)) {
+               list_init(list);
                return;
+       }
 
        list->next = head->next;
        list->next->prev = list;
@@ -166,31 +210,52 @@ static inline void list_cut(struct list_head *list, struct list_head *head)
        list_init(head);
 }
 
+static inline void list_splice_front(struct list_head *list, struct list_head *head)
+{
+       list->next->prev = head;
+       list->prev->next = head->next;
+       head->next->prev = list->prev;
+       head->next = list->next;
+}
+
 static inline void *usbi_reallocf(void *ptr, size_t size)
 {
        void *ret = realloc(ptr, size);
+
        if (!ret)
                free(ptr);
        return ret;
 }
 
-#define container_of(ptr, type, member) ({                     \
-       const typeof( ((type *)0)->member ) *mptr = (ptr);      \
-       (type *)( (char *)mptr - offsetof(type,member) );})
-
-#ifndef CLAMP
-#define CLAMP(val, min, max) ((val) < (min) ? (min) : ((val) > (max) ? (max) : (val)))
-#endif
-#ifndef MIN
-#define MIN(a, b)      ((a) < (b) ? (a) : (b))
+#if !defined(USEC_PER_SEC)
+#define USEC_PER_SEC   1000000L
 #endif
-#ifndef MAX
-#define MAX(a, b)      ((a) > (b) ? (a) : (b))
+
+#if !defined(NSEC_PER_SEC)
+#define NSEC_PER_SEC   1000000000L
 #endif
 
-#define TIMESPEC_IS_SET(ts) ((ts)->tv_sec != 0 || (ts)->tv_nsec != 0)
+#define TIMEVAL_IS_VALID(tv)                                           \
+       ((tv)->tv_sec >= 0 &&                                           \
+        (tv)->tv_usec >= 0 && (tv)->tv_usec < USEC_PER_SEC)
+
+#define TIMESPEC_IS_SET(ts)    ((ts)->tv_sec || (ts)->tv_nsec)
+#define TIMESPEC_CLEAR(ts)     (ts)->tv_sec = (ts)->tv_nsec = 0
+#define TIMESPEC_CMP(a, b, CMP)                                                \
+       (((a)->tv_sec == (b)->tv_sec)                                   \
+        ? ((a)->tv_nsec CMP (b)->tv_nsec)                              \
+        : ((a)->tv_sec CMP (b)->tv_sec))
+#define TIMESPEC_SUB(a, b, result)                                     \
+       do {                                                            \
+               (result)->tv_sec = (a)->tv_sec - (b)->tv_sec;           \
+               (result)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec;        \
+               if ((result)->tv_nsec < 0L) {                           \
+                       --(result)->tv_sec;                             \
+                       (result)->tv_nsec += NSEC_PER_SEC;              \
+               }                                                       \
+       } while (0)
 
-#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
+#if defined(PLATFORM_WINDOWS)
 #define TIMEVAL_TV_SEC_TYPE    long
 #else
 #define TIMEVAL_TV_SEC_TYPE    time_t
@@ -201,102 +266,66 @@ static inline void *usbi_reallocf(void *ptr, size_t size)
 #define TIMESPEC_TO_TIMEVAL(tv, ts)                                    \
        do {                                                            \
                (tv)->tv_sec = (TIMEVAL_TV_SEC_TYPE) (ts)->tv_sec;      \
-               (tv)->tv_usec = (ts)->tv_nsec / 1000                  \
+               (tv)->tv_usec = (ts)->tv_nsec / 1000L;                  \
        } while (0)
 #endif
 
 #ifdef ENABLE_LOGGING
 
 #if defined(_MSC_VER) && (_MSC_VER < 1900)
+#include <stdio.h>
 #define snprintf usbi_snprintf
 #define vsnprintf usbi_vsnprintf
 int usbi_snprintf(char *dst, size_t size, const char *format, ...);
-int usbi_vsnprintf(char *dst, size_t size, const char *format, va_list ap);
+int usbi_vsnprintf(char *dst, size_t size, const char *format, va_list args);
 #define LIBUSB_PRINTF_WIN32
 #endif /* defined(_MSC_VER) && (_MSC_VER < 1900) */
 
 void usbi_log(struct libusb_context *ctx, enum libusb_log_level level,
-       const char *function, const char *format, ...);
-
-void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
-       const char *function, const char *format, va_list args);
-
-#if !defined(_MSC_VER) || (_MSC_VER >= 1400)
-
-#define _usbi_log(ctx, level, ...) usbi_log(ctx, level, __FUNCTION__, __VA_ARGS__)
+       const char *function, const char *format, ...) PRINTF_FORMAT(4, 5);
 
-#define usbi_err(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_ERROR, __VA_ARGS__)
-#define usbi_warn(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_WARNING, __VA_ARGS__)
-#define usbi_info(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_INFO, __VA_ARGS__)
-#define usbi_dbg(...) _usbi_log(NULL, LIBUSB_LOG_LEVEL_DEBUG, __VA_ARGS__)
+#define _usbi_log(ctx, level, ...) usbi_log(ctx, level, __func__, __VA_ARGS__)
 
-#else /* !defined(_MSC_VER) || (_MSC_VER >= 1400) */
-
-#define LOG_BODY(ctxt, level)                          \
-{                                                      \
-       va_list args;                                   \
-       va_start(args, format);                         \
-       usbi_log_v(ctxt, level, "", format, args);      \
-       va_end(args);                                   \
-}
-
-static inline void usbi_err(struct libusb_context *ctx, const char *format, ...)
-       LOG_BODY(ctx, LIBUSB_LOG_LEVEL_ERROR)
-static inline void usbi_warn(struct libusb_context *ctx, const char *format, ...)
-       LOG_BODY(ctx, LIBUSB_LOG_LEVEL_WARNING)
-static inline void usbi_info(struct libusb_context *ctx, const char *format, ...)
-       LOG_BODY(ctx, LIBUSB_LOG_LEVEL_INFO)
-static inline void usbi_dbg(const char *format, ...)
-       LOG_BODY(NULL, LIBUSB_LOG_LEVEL_DEBUG)
-
-#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1400) */
+#define usbi_err(ctx, ...)     _usbi_log(ctx, LIBUSB_LOG_LEVEL_ERROR, __VA_ARGS__)
+#define usbi_warn(ctx, ...)    _usbi_log(ctx, LIBUSB_LOG_LEVEL_WARNING, __VA_ARGS__)
+#define usbi_info(ctx, ...)    _usbi_log(ctx, LIBUSB_LOG_LEVEL_INFO, __VA_ARGS__)
+#define usbi_dbg(...)          _usbi_log(NULL, LIBUSB_LOG_LEVEL_DEBUG, __VA_ARGS__)
 
 #else /* ENABLE_LOGGING */
 
-#define usbi_err(ctx, ...) do { (void)ctx; } while (0)
-#define usbi_warn(ctx, ...) do { (void)ctx; } while (0)
-#define usbi_info(ctx, ...) do { (void)ctx; } while (0)
-#define usbi_dbg(...) do {} while (0)
+#define usbi_err(ctx, ...)     UNUSED(ctx)
+#define usbi_warn(ctx, ...)    UNUSED(ctx)
+#define usbi_info(ctx, ...)    UNUSED(ctx)
+#define usbi_dbg(...)          do {} while (0)
 
 #endif /* ENABLE_LOGGING */
 
-#define USBI_GET_CONTEXT(ctx)                          \
-       do {                                            \
-               if (!(ctx))                             \
-                       (ctx) = usbi_default_context;   \
-       } while(0)
-
 #define DEVICE_CTX(dev)                ((dev)->ctx)
 #define HANDLE_CTX(handle)     (DEVICE_CTX((handle)->dev))
 #define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle))
-#define ITRANSFER_CTX(transfer) \
-       (TRANSFER_CTX(USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)))
+#define ITRANSFER_CTX(itransfer) \
+       (TRANSFER_CTX(USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)))
 
 #define IS_EPIN(ep)            (0 != ((ep) & LIBUSB_ENDPOINT_IN))
 #define IS_EPOUT(ep)           (!IS_EPIN(ep))
 #define IS_XFERIN(xfer)                (0 != ((xfer)->endpoint & LIBUSB_ENDPOINT_IN))
 #define IS_XFEROUT(xfer)       (!IS_XFERIN(xfer))
 
-/* Internal abstraction for thread synchronization */
-#if defined(THREADS_POSIX)
-#include "os/threads_posix.h"
-#elif defined(OS_WINDOWS) || defined(OS_WINCE)
-#include "os/threads_windows.h"
-#endif
-
-extern struct libusb_context *usbi_default_context;
-
-/* Forward declaration for use in context (fully defined inside poll abstraction) */
-struct pollfd;
-
 struct libusb_context {
 #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
        enum libusb_log_level debug;
        int debug_fixed;
+       libusb_log_cb log_handler;
 #endif
 
-       /* internal event pipe, used for signalling occurrence of an internal event. */
-       int event_pipe[2];
+       /* used for signalling occurrence of an internal event. */
+       usbi_event_t event;
+
+#ifdef HAVE_OS_TIMER
+       /* used for timeout handling, if supported by OS.
+        * this timer is maintained to trigger on the next pending timeout */
+       usbi_timer_t timer;
+#endif
 
        struct list_head usb_devs;
        usbi_mutex_t usb_devs_lock;
@@ -320,10 +349,12 @@ struct libusb_context {
         * take this lock first */
        usbi_mutex_t flying_transfers_lock;
 
+#if !defined(PLATFORM_WINDOWS)
        /* user callbacks for pollfd changes */
        libusb_pollfd_added_cb fd_added_cb;
        libusb_pollfd_removed_cb fd_removed_cb;
        void *fd_cb_user_data;
+#endif
 
        /* ensures that only one thread is handling events at any one time */
        usbi_mutex_t events_lock;
@@ -351,11 +382,17 @@ struct libusb_context {
         * in order to safely close a device. Protected by event_data_lock. */
        unsigned int device_close;
 
-       /* list and count of poll fds and an array of poll fd structures that is
-        * (re)allocated as necessary prior to polling. Protected by event_data_lock. */
-       struct list_head ipollfds;
-       struct pollfd *pollfds;
-       POLL_NFDS_TYPE pollfds_cnt;
+       /* A list of currently active event sources. Protected by event_data_lock. */
+       struct list_head event_sources;
+
+       /* A list of event sources that have been removed since the last time
+        * event sources were waited on. Protected by event_data_lock. */
+       struct list_head removed_event_sources;
+
+       /* A pointer and count to platform-specific data used for monitoring event
+        * sources. Only accessed during event handling. */
+       void *event_data;
+       unsigned int event_data_cnt;
 
        /* A list of pending hotplug messages. Protected by event_data_lock. */
        struct list_head hotplug_msgs;
@@ -363,48 +400,54 @@ struct libusb_context {
        /* A list of pending completed transfers. Protected by event_data_lock. */
        struct list_head completed_transfers;
 
-#ifdef USBI_TIMERFD_AVAILABLE
-       /* used for timeout handling, if supported by OS.
-        * this timerfd is maintained to trigger on the next pending timeout */
-       int timerfd;
-#endif
-
        struct list_head list;
-
-       PTR_ALIGNED unsigned char os_priv[ZERO_SIZED_ARRAY];
 };
 
+extern struct libusb_context *usbi_default_context;
+
+extern struct list_head active_contexts_list;
+extern usbi_mutex_static_t active_contexts_lock;
+
+static inline struct libusb_context *usbi_get_context(struct libusb_context *ctx)
+{
+       return ctx ? ctx : usbi_default_context;
+}
+
 enum usbi_event_flags {
-       /* The list of pollfds has been modified */
-       USBI_EVENT_POLLFDS_MODIFIED = 1 << 0,
+       /* The list of event sources has been modified */
+       USBI_EVENT_EVENT_SOURCES_MODIFIED = 1U << 0,
 
        /* The user has interrupted the event handler */
-       USBI_EVENT_USER_INTERRUPT = 1 << 1,
+       USBI_EVENT_USER_INTERRUPT = 1U << 1,
 
        /* A hotplug callback deregistration is pending */
-       USBI_EVENT_HOTPLUG_CB_DEREGISTERED = 1 << 2,
-};
+       USBI_EVENT_HOTPLUG_CB_DEREGISTERED = 1U << 2,
 
-/* Macros for managing event handling state */
-#define usbi_handling_events(ctx) \
-       (usbi_tls_key_get((ctx)->event_handling_key) != NULL)
+       /* One or more hotplug messages are pending */
+       USBI_EVENT_HOTPLUG_MSG_PENDING = 1U << 3,
 
-#define usbi_start_event_handling(ctx) \
-       usbi_tls_key_set((ctx)->event_handling_key, ctx)
+       /* One or more completed transfers are pending */
+       USBI_EVENT_TRANSFER_COMPLETED = 1U << 4,
 
-#define usbi_end_event_handling(ctx) \
-       usbi_tls_key_set((ctx)->event_handling_key, NULL)
+       /* A device is in the process of being closed */
+       USBI_EVENT_DEVICE_CLOSE = 1U << 5,
+};
 
-/* Update the following macro if new event sources are added */
-#define usbi_pending_events(ctx) \
-       ((ctx)->event_flags || (ctx)->device_close \
-        || !list_empty(&(ctx)->hotplug_msgs) || !list_empty(&(ctx)->completed_transfers))
+/* Macros for managing event handling state */
+static inline int usbi_handling_events(struct libusb_context *ctx)
+{
+       return usbi_tls_key_get(ctx->event_handling_key) != NULL;
+}
 
-#ifdef USBI_TIMERFD_AVAILABLE
-#define usbi_using_timerfd(ctx) ((ctx)->timerfd >= 0)
-#else
-#define usbi_using_timerfd(ctx) (0)
-#endif
+static inline void usbi_start_event_handling(struct libusb_context *ctx)
+{
+       usbi_tls_key_set(ctx->event_handling_key, ctx);
+}
+
+static inline void usbi_end_event_handling(struct libusb_context *ctx)
+{
+       usbi_tls_key_set(ctx->event_handling_key, NULL);
+}
 
 struct libusb_device {
        /* lock protects refcnt, everything else is finalized at initialization
@@ -413,12 +456,11 @@ struct libusb_device {
        int refcnt;
 
        struct libusb_context *ctx;
+       struct libusb_device *parent_dev;
 
        uint8_t bus_number;
        uint8_t port_number;
-       struct libusb_device* parent_dev;
        uint8_t device_address;
-       uint8_t num_configurations;
        enum libusb_speed speed;
 
        struct list_head list;
@@ -426,8 +468,6 @@ struct libusb_device {
 
        struct libusb_device_descriptor device_descriptor;
        int attached;
-
-       PTR_ALIGNED unsigned char os_priv[ZERO_SIZED_ARRAY];
 };
 
 struct libusb_device_handle {
@@ -438,37 +478,61 @@ struct libusb_device_handle {
        struct list_head list;
        struct libusb_device *dev;
        int auto_detach_kernel_driver;
-
-       PTR_ALIGNED unsigned char os_priv[ZERO_SIZED_ARRAY];
 };
 
-enum {
-       USBI_CLOCK_MONOTONIC,
-       USBI_CLOCK_REALTIME
-};
+/* Function called by backend during device initialization to convert
+ * multi-byte fields in the device descriptor to host-endian format.
+ */
+static inline void usbi_localize_device_descriptor(struct libusb_device_descriptor *desc)
+{
+       desc->bcdUSB = libusb_le16_to_cpu(desc->bcdUSB);
+       desc->idVendor = libusb_le16_to_cpu(desc->idVendor);
+       desc->idProduct = libusb_le16_to_cpu(desc->idProduct);
+       desc->bcdDevice = libusb_le16_to_cpu(desc->bcdDevice);
+}
+
+#ifdef HAVE_CLOCK_GETTIME
+static inline void usbi_get_monotonic_time(struct timespec *tp)
+{
+       ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, tp), 0);
+}
+static inline void usbi_get_real_time(struct timespec *tp)
+{
+       ASSERT_EQ(clock_gettime(CLOCK_REALTIME, tp), 0);
+}
+#else
+/* If the platform doesn't provide the clock_gettime() function, the backend
+ * must provide its own clock implementations.  Two clock functions are
+ * required:
+ *
+ *   usbi_get_monotonic_time(): returns the time since an unspecified starting
+ *                              point (usually boot) that is monotonically
+ *                              increasing.
+ *   usbi_get_real_time(): returns the time since system epoch.
+ */
+void usbi_get_monotonic_time(struct timespec *tp);
+void usbi_get_real_time(struct timespec *tp);
+#endif
 
 /* in-memory transfer layout:
  *
- * 1. struct usbi_transfer
- * 2. struct libusb_transfer (which includes iso packets) [variable size]
- * 3. os private data [variable size]
+ * 1. os private data
+ * 2. struct usbi_transfer
+ * 3. struct libusb_transfer (which includes iso packets) [variable size]
  *
  * from a libusb_transfer, you can get the usbi_transfer by rewinding the
  * appropriate number of bytes.
- * the usbi_transfer includes the number of allocated packets, so you can
- * determine the size of the transfer and hence the start and length of the
- * OS-private data.
  */
 
 struct usbi_transfer {
        int num_iso_packets;
        struct list_head list;
        struct list_head completed_list;
-       struct timeval timeout;
+       struct timespec timeout;
        int transferred;
        uint32_t stream_id;
-       uint8_t state_flags;   /* Protected by usbi_transfer->lock */
-       uint8_t timeout_flags; /* Protected by the flying_stransfers_lock */
+       uint32_t state_flags;   /* Protected by usbi_transfer->lock */
+       uint32_t timeout_flags; /* Protected by the flying_stransfers_lock */
 
        /* this lock is held during libusb_submit_transfer() and
         * libusb_cancel_transfer() (allowing the OS backend to prevent duplicate
@@ -480,51 +544,124 @@ struct usbi_transfer {
         * Note paths taking both this and the flying_transfers_lock must
         * always take the flying_transfers_lock first */
        usbi_mutex_t lock;
+
+       void *priv;
 };
 
 enum usbi_transfer_state_flags {
        /* Transfer successfully submitted by backend */
-       USBI_TRANSFER_IN_FLIGHT = 1 << 0,
+       USBI_TRANSFER_IN_FLIGHT = 1U << 0,
 
        /* Cancellation was requested via libusb_cancel_transfer() */
-       USBI_TRANSFER_CANCELLING = 1 << 1,
+       USBI_TRANSFER_CANCELLING = 1U << 1,
 
        /* Operation on the transfer failed because the device disappeared */
-       USBI_TRANSFER_DEVICE_DISAPPEARED = 1 << 2,
+       USBI_TRANSFER_DEVICE_DISAPPEARED = 1U << 2,
 };
 
 enum usbi_transfer_timeout_flags {
        /* Set by backend submit_transfer() if the OS handles timeout */
-       USBI_TRANSFER_OS_HANDLES_TIMEOUT = 1 << 0,
+       USBI_TRANSFER_OS_HANDLES_TIMEOUT = 1U << 0,
 
        /* The transfer timeout has been handled */
-       USBI_TRANSFER_TIMEOUT_HANDLED = 1 << 1,
+       USBI_TRANSFER_TIMEOUT_HANDLED = 1U << 1,
 
        /* The transfer timeout was successfully processed */
-       USBI_TRANSFER_TIMED_OUT = 1 << 2,
+       USBI_TRANSFER_TIMED_OUT = 1U << 2,
 };
 
-#define USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)                     \
-       ((struct libusb_transfer *)(((unsigned char *)(transfer))       \
-               + sizeof(struct usbi_transfer)))
-#define LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer)                     \
-       ((struct usbi_transfer *)(((unsigned char *)(transfer))         \
-               - sizeof(struct usbi_transfer)))
+#define USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)    \
+       ((struct libusb_transfer *)                     \
+        ((unsigned char *)(itransfer)                  \
+         + PTR_ALIGN(sizeof(struct usbi_transfer))))
+#define LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer)     \
+       ((struct usbi_transfer *)                       \
+        ((unsigned char *)(transfer)                   \
+         - PTR_ALIGN(sizeof(struct usbi_transfer))))
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif
 
-static inline void *usbi_transfer_get_os_priv(struct usbi_transfer *transfer)
-{
-       return ((unsigned char *)transfer) + sizeof(struct usbi_transfer)
-               + sizeof(struct libusb_transfer)
-               + (transfer->num_iso_packets
-                       * sizeof(struct libusb_iso_packet_descriptor));
-}
+/* All standard descriptors have these 2 fields in common */
+struct usbi_descriptor_header {
+       uint8_t  bLength;
+       uint8_t  bDescriptorType;
+} LIBUSB_PACKED;
+
+struct usbi_device_descriptor {
+       uint8_t  bLength;
+       uint8_t  bDescriptorType;
+       uint16_t bcdUSB;
+       uint8_t  bDeviceClass;
+       uint8_t  bDeviceSubClass;
+       uint8_t  bDeviceProtocol;
+       uint8_t  bMaxPacketSize0;
+       uint16_t idVendor;
+       uint16_t idProduct;
+       uint16_t bcdDevice;
+       uint8_t  iManufacturer;
+       uint8_t  iProduct;
+       uint8_t  iSerialNumber;
+       uint8_t  bNumConfigurations;
+} LIBUSB_PACKED;
+
+struct usbi_configuration_descriptor {
+       uint8_t  bLength;
+       uint8_t  bDescriptorType;
+       uint16_t wTotalLength;
+       uint8_t  bNumInterfaces;
+       uint8_t  bConfigurationValue;
+       uint8_t  iConfiguration;
+       uint8_t  bmAttributes;
+       uint8_t  bMaxPower;
+} LIBUSB_PACKED;
+
+struct usbi_interface_descriptor {
+       uint8_t  bLength;
+       uint8_t  bDescriptorType;
+       uint8_t  bInterfaceNumber;
+       uint8_t  bAlternateSetting;
+       uint8_t  bNumEndpoints;
+       uint8_t  bInterfaceClass;
+       uint8_t  bInterfaceSubClass;
+       uint8_t  bInterfaceProtocol;
+       uint8_t  iInterface;
+} LIBUSB_PACKED;
+
+struct usbi_string_descriptor {
+       uint8_t  bLength;
+       uint8_t  bDescriptorType;
+       uint16_t wData[ZERO_SIZED_ARRAY];
+} LIBUSB_PACKED;
+
+struct usbi_bos_descriptor {
+       uint8_t  bLength;
+       uint8_t  bDescriptorType;
+       uint16_t wTotalLength;
+       uint8_t  bNumDeviceCaps;
+} LIBUSB_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif
 
-/* bus structures */
+union usbi_config_desc_buf {
+        struct usbi_configuration_descriptor desc;
+        uint8_t buf[LIBUSB_DT_CONFIG_SIZE];
+        uint16_t align;         /* Force 2-byte alignment */
+};
 
-/* All standard descriptors have these 2 fields in common */
-struct usb_descriptor_header {
-       uint8_t bLength;
-       uint8_t bDescriptorType;
+union usbi_string_desc_buf {
+        struct usbi_string_descriptor desc;
+        uint8_t buf[255];       /* Some devices choke on size > 255 */
+        uint16_t align;         /* Force 2-byte alignment */
+};
+
+union usbi_bos_desc_buf {
+        struct usbi_bos_descriptor desc;
+        uint8_t buf[LIBUSB_DT_BOS_SIZE];
+        uint16_t align;         /* Force 2-byte alignment */
 };
 
 /* shared data and functions */
@@ -541,39 +678,88 @@ void usbi_handle_disconnect(struct libusb_device_handle *dev_handle);
 
 int usbi_handle_transfer_completion(struct usbi_transfer *itransfer,
        enum libusb_transfer_status status);
-int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer);
-void usbi_signal_transfer_completion(struct usbi_transfer *transfer);
-
-int usbi_parse_descriptor(const unsigned char *source, const char *descriptor,
-       void *dest, int host_endian);
-int usbi_device_cache_descriptor(libusb_device *dev);
-int usbi_get_config_index_by_value(struct libusb_device *dev,
-       uint8_t bConfigurationValue, int *idx);
-
-void usbi_connect_device (struct libusb_device *dev);
-void usbi_disconnect_device (struct libusb_device *dev);
-
-int usbi_signal_event(struct libusb_context *ctx);
-int usbi_clear_event(struct libusb_context *ctx);
-
-/* Internal abstraction for poll (needs struct usbi_transfer on Windows) */
-#if defined(OS_LINUX) || defined(OS_DARWIN) || defined(OS_OPENBSD) || defined(OS_NETBSD) ||\
-       defined(OS_HAIKU) || defined(OS_SUNOS)
-#include <unistd.h>
-#include "os/poll_posix.h"
-#elif defined(OS_WINDOWS) || defined(OS_WINCE)
-#include "os/poll_windows.h"
-#endif
+int usbi_handle_transfer_cancellation(struct usbi_transfer *itransfer);
+void usbi_signal_transfer_completion(struct usbi_transfer *itransfer);
 
-struct usbi_pollfd {
-       /* must come first */
-       struct libusb_pollfd pollfd;
+void usbi_connect_device(struct libusb_device *dev);
+void usbi_disconnect_device(struct libusb_device *dev);
 
+struct usbi_event_source {
+       struct usbi_event_source_data {
+               usbi_os_handle_t os_handle;
+               short poll_events;
+       } data;
        struct list_head list;
 };
 
-int usbi_add_pollfd(struct libusb_context *ctx, int fd, short events);
-void usbi_remove_pollfd(struct libusb_context *ctx, int fd);
+int usbi_add_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle,
+       short poll_events);
+void usbi_remove_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle);
+
+/* OS event abstraction */
+
+int usbi_create_event(usbi_event_t *event);
+void usbi_destroy_event(usbi_event_t *event);
+void usbi_signal_event(usbi_event_t *event);
+void usbi_clear_event(usbi_event_t *event);
+
+#ifdef HAVE_OS_TIMER
+int usbi_create_timer(usbi_timer_t *timer);
+void usbi_destroy_timer(usbi_timer_t *timer);
+int usbi_arm_timer(usbi_timer_t *timer, const struct timespec *timeout);
+int usbi_disarm_timer(usbi_timer_t *timer);
+#endif
+
+static inline int usbi_using_timer(struct libusb_context *ctx)
+{
+#ifdef HAVE_OS_TIMER
+       return usbi_timer_valid(&ctx->timer);
+#else
+       UNUSED(ctx);
+       return 0;
+#endif
+}
+
+struct usbi_reported_events {
+       union {
+               struct {
+                       unsigned int event_triggered:1;
+#ifdef HAVE_OS_TIMER
+                       unsigned int timer_triggered:1;
+#endif
+               };
+               unsigned int event_bits;
+       };
+       void *event_data;
+       unsigned int event_data_count;
+       unsigned int num_ready;
+};
+
+int usbi_alloc_event_data(struct libusb_context *ctx);
+int usbi_wait_for_events(struct libusb_context *ctx,
+       struct usbi_reported_events *reported_events, int timeout_ms);
+
+/* accessor functions for structure private data */
+
+static inline void *usbi_get_context_priv(struct libusb_context *ctx)
+{
+       return (unsigned char *)ctx + PTR_ALIGN(sizeof(*ctx));
+}
+
+static inline void *usbi_get_device_priv(struct libusb_device *dev)
+{
+       return (unsigned char *)dev + PTR_ALIGN(sizeof(*dev));
+}
+
+static inline void *usbi_get_device_handle_priv(struct libusb_device_handle *dev_handle)
+{
+       return (unsigned char *)dev_handle + PTR_ALIGN(sizeof(*dev_handle));
+}
+
+static inline void *usbi_get_transfer_priv(struct usbi_transfer *itransfer)
+{
+       return itransfer->priv;
+}
 
 /* device discovery */
 
@@ -701,6 +887,34 @@ struct usbi_os_backend {
         */
        void (*hotplug_poll)(void);
 
+       /* Wrap a platform-specific device handle for I/O and other USB
+        * operations. The device handle is preallocated for you.
+        *
+        * Your backend should allocate any internal resources required for I/O
+        * and other operations so that those operations can happen (hopefully)
+        * without hiccup. This is also a good place to inform libusb that it
+        * should monitor certain file descriptors related to this device -
+        * see the usbi_add_event_source() function.
+        *
+        * Your backend should also initialize the device structure
+        * (dev_handle->dev), which is NULL at the beginning of the call.
+        *
+        * This function should not generate any bus I/O and should not block.
+        *
+        * This function is called when the user attempts to wrap an existing
+        * platform-specific device handle for a device.
+        *
+        * Return:
+        * - 0 on success
+        * - LIBUSB_ERROR_ACCESS if the user has insufficient permissions
+        * - another LIBUSB_ERROR code on other failure
+        *
+        * Do not worry about freeing the handle on failed open, the upper layers
+        * do this for you.
+        */
+       int (*wrap_sys_device)(struct libusb_context *ctx,
+               struct libusb_device_handle *dev_handle, intptr_t sys_dev);
+
        /* Open a device for I/O and other USB operations. The device handle
         * is preallocated for you, you can retrieve the device in question
         * through handle->dev.
@@ -709,7 +923,7 @@ struct usbi_os_backend {
         * and other operations so that those operations can happen (hopefully)
         * without hiccup. This is also a good place to inform libusb that it
         * should monitor certain file descriptors related to this device -
-        * see the usbi_add_pollfd() function.
+        * see the usbi_add_event_source() function.
         *
         * This function should not generate any bus I/O and should not block.
         *
@@ -730,38 +944,14 @@ struct usbi_os_backend {
 
        /* Close a device such that the handle cannot be used again. Your backend
         * should destroy any resources that were allocated in the open path.
-        * This may also be a good place to call usbi_remove_pollfd() to inform
-        * libusb of any file descriptors associated with this device that should
-        * no longer be monitored.
+        * This may also be a good place to call usbi_remove_event_source() to
+        * inform libusb of any event sources associated with this device that
+        * should no longer be monitored.
         *
         * This function is called when the user closes a device handle.
         */
        void (*close)(struct libusb_device_handle *dev_handle);
 
-       /* Retrieve the device descriptor from a device.
-        *
-        * The descriptor should be retrieved from memory, NOT via bus I/O to the
-        * device. This means that you may have to cache it in a private structure
-        * during get_device_list enumeration. Alternatively, you may be able
-        * to retrieve it from a kernel interface (some Linux setups can do this)
-        * still without generating bus I/O.
-        *
-        * This function is expected to write DEVICE_DESC_LENGTH (18) bytes into
-        * buffer, which is guaranteed to be big enough.
-        *
-        * This function is called when sanity-checking a device before adding
-        * it to the list of discovered devices, and also when the user requests
-        * to read the device descriptor.
-        *
-        * This function is expected to return the descriptor in bus-endian format
-        * (LE). If it returns the multi-byte values in host-endian format,
-        * set the host_endian output parameter to "1".
-        *
-        * Return 0 on success or a LIBUSB_ERROR code on failure.
-        */
-       int (*get_device_descriptor)(struct libusb_device *device,
-               unsigned char *buffer, int *host_endian);
-
        /* Get the ACTIVE configuration descriptor for a device.
         *
         * The descriptor should be retrieved from memory, NOT via bus I/O to the
@@ -774,8 +964,7 @@ struct usbi_os_backend {
         * return an error code.
         *
         * This function is expected to return the descriptor in bus-endian format
-        * (LE). If it returns the multi-byte values in host-endian format,
-        * set the host_endian output parameter to "1".
+        * (LE).
         *
         * Return:
         * - 0 on success
@@ -783,7 +972,7 @@ struct usbi_os_backend {
         * - another LIBUSB_ERROR code on other failure
         */
        int (*get_active_config_descriptor)(struct libusb_device *device,
-               unsigned char *buffer, size_t len, int *host_endian);
+               void *buffer, size_t len);
 
        /* Get a specific configuration descriptor for a device.
         *
@@ -801,14 +990,12 @@ struct usbi_os_backend {
         * return an error code.
         *
         * This function is expected to return the descriptor in bus-endian format
-        * (LE). If it returns the multi-byte values in host-endian format,
-        * set the host_endian output parameter to "1".
+        * (LE).
         *
         * Return the length read on success or a LIBUSB_ERROR code on failure.
         */
        int (*get_config_descriptor)(struct libusb_device *device,
-               uint8_t config_index, unsigned char *buffer, size_t len,
-               int *host_endian);
+               uint8_t config_index, void *buffer, size_t len);
 
        /* Like get_config_descriptor but then by bConfigurationValue instead
         * of by index.
@@ -823,8 +1010,7 @@ struct usbi_os_backend {
         * or a LIBUSB_ERROR code on failure.
         */
        int (*get_config_descriptor_by_value)(struct libusb_device *device,
-               uint8_t bConfigurationValue, unsigned char **buffer,
-               int *host_endian);
+               uint8_t bConfigurationValue, void **buffer);
 
        /* Get the bConfigurationValue for the active configuration for a device.
         * Optional. This should only be implemented if you can retrieve it from
@@ -843,7 +1029,7 @@ struct usbi_os_backend {
         *   blocking
         * - another LIBUSB_ERROR code on other failure.
         */
-       int (*get_configuration)(struct libusb_device_handle *dev_handle, int *config);
+       int (*get_configuration)(struct libusb_device_handle *dev_handle, uint8_t *config);
 
        /* Set the active configuration for a device.
         *
@@ -879,7 +1065,7 @@ struct usbi_os_backend {
         *   was opened
         * - another LIBUSB_ERROR code on other failure
         */
-       int (*claim_interface)(struct libusb_device_handle *dev_handle, int interface_number);
+       int (*claim_interface)(struct libusb_device_handle *dev_handle, uint8_t interface_number);
 
        /* Release a previously claimed interface.
         *
@@ -896,7 +1082,7 @@ struct usbi_os_backend {
         *   was opened
         * - another LIBUSB_ERROR code on other failure
         */
-       int (*release_interface)(struct libusb_device_handle *dev_handle, int interface_number);
+       int (*release_interface)(struct libusb_device_handle *dev_handle, uint8_t interface_number);
 
        /* Set the alternate setting for an interface.
         *
@@ -913,7 +1099,7 @@ struct usbi_os_backend {
         * - another LIBUSB_ERROR code on other failure
         */
        int (*set_interface_altsetting)(struct libusb_device_handle *dev_handle,
-               int interface_number, int altsetting);
+               uint8_t interface_number, uint8_t altsetting);
 
        /* Clear a halt/stall condition on an endpoint.
         *
@@ -929,13 +1115,13 @@ struct usbi_os_backend {
        int (*clear_halt)(struct libusb_device_handle *dev_handle,
                unsigned char endpoint);
 
-       /* Perform a USB port reset to reinitialize a device.
+       /* Perform a USB port reset to reinitialize a device. Optional.
         *
         * If possible, the device handle should still be usable after the reset
         * completes, assuming that the device descriptors did not change during
         * reset and all previous interface state can be restored.
         *
-        * If something changes, or you cannot easily locate/verify the resetted
+        * If something changes, or you cannot easily locate/verify the reset
         * device, return LIBUSB_ERROR_NOT_FOUND. This prompts the application
         * to close the old handle and re-enumerate the device.
         *
@@ -958,12 +1144,11 @@ struct usbi_os_backend {
        /* Allocate persistent DMA memory for the given device, suitable for
         * zerocopy. May return NULL on failure. Optional to implement.
         */
-       unsigned char *(*dev_mem_alloc)(struct libusb_device_handle *handle,
-               size_t len);
+       void *(*dev_mem_alloc)(struct libusb_device_handle *handle, size_t len);
 
        /* Free memory allocated by dev_mem_alloc. */
-       int (*dev_mem_free)(struct libusb_device_handle *handle,
-               unsigned char *buffer, size_t len);
+       int (*dev_mem_free)(struct libusb_device_handle *handle, void *buffer,
+               size_t len);
 
        /* Determine if a kernel driver is active on an interface. Optional.
         *
@@ -978,7 +1163,7 @@ struct usbi_os_backend {
         * - another LIBUSB_ERROR code on other failure
         */
        int (*kernel_driver_active)(struct libusb_device_handle *dev_handle,
-               int interface_number);
+               uint8_t interface_number);
 
        /* Detach a kernel driver from an interface. Optional.
         *
@@ -994,7 +1179,7 @@ struct usbi_os_backend {
         * - another LIBUSB_ERROR code on other failure
         */
        int (*detach_kernel_driver)(struct libusb_device_handle *dev_handle,
-               int interface_number);
+               uint8_t interface_number);
 
        /* Attach a kernel driver to an interface. Optional.
         *
@@ -1011,7 +1196,7 @@ struct usbi_os_backend {
         * - another LIBUSB_ERROR code on other failure
         */
        int (*attach_kernel_driver)(struct libusb_device_handle *dev_handle,
-               int interface_number);
+               uint8_t interface_number);
 
        /* Destroy a device. Optional.
         *
@@ -1056,21 +1241,22 @@ struct usbi_os_backend {
         */
        void (*clear_transfer_priv)(struct usbi_transfer *itransfer);
 
-       /* Handle any pending events on file descriptors. Optional.
+       /* Handle any pending events on event sources. Optional.
         *
-        * Provide this function when file descriptors directly indicate device
-        * or transfer activity. If your backend does not have such file descriptors,
+        * Provide this function when event sources directly indicate device
+        * or transfer activity. If your backend does not have such event sources,
         * implement the handle_transfer_completion function below.
         *
         * This involves monitoring any active transfers and processing their
         * completion or cancellation.
         *
-        * The function is passed an array of pollfd structures (size nfds)
-        * as a result of the poll() system call. The num_ready parameter
-        * indicates the number of file descriptors that have reported events
-        * (i.e. the poll() return value). This should be enough information
-        * for you to determine which actions need to be taken on the currently
-        * active transfers.
+        * The function is passed a pointer that represents platform-specific
+        * data for monitoring event sources (size count). This data is to be
+        * (re)allocated as necessary when event sources are modified.
+        * The num_ready parameter indicates the number of event sources that
+        * have reported events. This should be enough information for you to
+        * determine which actions need to be taken on the currently active
+        * transfers.
         *
         * For any cancelled transfers, call usbi_handle_transfer_cancellation().
         * For completed transfers, call usbi_handle_transfer_completion().
@@ -1089,13 +1275,13 @@ struct usbi_os_backend {
         * Return 0 on success, or a LIBUSB_ERROR code on failure.
         */
        int (*handle_events)(struct libusb_context *ctx,
-               struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready);
+               void *event_data, unsigned int count, unsigned int num_ready);
 
        /* Handle transfer completion. Optional.
         *
-        * Provide this function when there are no file descriptors available
-        * that directly indicate device or transfer activity. If your backend does
-        * have such file descriptors, implement the handle_events function above.
+        * Provide this function when there are no event sources available that
+        * directly indicate device or transfer activity. If your backend does
+        * have such event sources, implement the handle_events function above.
         *
         * Your backend must tell the library when a transfer has completed by
         * calling usbi_signal_transfer_completion(). You should store any private
@@ -1116,47 +1302,68 @@ struct usbi_os_backend {
         */
        int (*handle_transfer_completion)(struct usbi_transfer *itransfer);
 
-       /* Get time from specified clock. At least two clocks must be implemented
-          by the backend: USBI_CLOCK_REALTIME, and USBI_CLOCK_MONOTONIC.
-
-          Description of clocks:
-            USBI_CLOCK_REALTIME : clock returns time since system epoch.
-            USBI_CLOCK_MONOTONIC: clock returns time since unspecified start
-                                    time (usually boot).
-        */
-       int (*clock_gettime)(int clkid, struct timespec *tp);
-
-#ifdef USBI_TIMERFD_AVAILABLE
-       /* clock ID of the clock that should be used for timerfd */
-       clockid_t (*get_timerfd_clockid)(void);
-#endif
-
        /* Number of bytes to reserve for per-context private backend data.
-        * This private data area is accessible through the "os_priv" field of
-        * struct libusb_context. */
+        * This private data area is accessible by calling
+        * usbi_get_context_priv() on the libusb_context instance.
+        */
        size_t context_priv_size;
 
        /* Number of bytes to reserve for per-device private backend data.
-        * This private data area is accessible through the "os_priv" field of
-        * struct libusb_device. */
+        * This private data area is accessible by calling
+        * usbi_get_device_priv() on the libusb_device instance.
+        */
        size_t device_priv_size;
 
        /* Number of bytes to reserve for per-handle private backend data.
-        * This private data area is accessible through the "os_priv" field of
-        * struct libusb_device. */
+        * This private data area is accessible by calling
+        * usbi_get_device_handle_priv() on the libusb_device_handle instance.
+        */
        size_t device_handle_priv_size;
 
        /* Number of bytes to reserve for per-transfer private backend data.
         * This private data area is accessible by calling
-        * usbi_transfer_get_os_priv() on the appropriate usbi_transfer instance.
+        * usbi_get_transfer_priv() on the usbi_transfer instance.
         */
        size_t transfer_priv_size;
 };
 
 extern const struct usbi_os_backend usbi_backend;
 
-extern struct list_head active_contexts_list;
-extern usbi_mutex_static_t active_contexts_lock;
+#define for_each_context(c) \
+       for_each_helper(c, &active_contexts_list, struct libusb_context)
+
+#define for_each_device(ctx, d) \
+       for_each_helper(d, &(ctx)->usb_devs, struct libusb_device)
+
+#define for_each_device_safe(ctx, d, n) \
+       for_each_safe_helper(d, n, &(ctx)->usb_devs, struct libusb_device)
+
+#define for_each_open_device(ctx, h) \
+       for_each_helper(h, &(ctx)->open_devs, struct libusb_device_handle)
+
+#define __for_each_transfer(list, t) \
+       for_each_helper(t, (list), struct usbi_transfer)
+
+#define for_each_transfer(ctx, t) \
+       __for_each_transfer(&(ctx)->flying_transfers, t)
+
+#define __for_each_transfer_safe(list, t, n) \
+       for_each_safe_helper(t, n, (list), struct usbi_transfer)
+
+#define for_each_transfer_safe(ctx, t, n) \
+       __for_each_transfer_safe(&(ctx)->flying_transfers, t, n)
+
+#define __for_each_completed_transfer_safe(list, t, n) \
+       list_for_each_entry_safe(t, n, (list), completed_list, struct usbi_transfer)
+
+#define for_each_event_source(ctx, e) \
+       for_each_helper(e, &(ctx)->event_sources, struct usbi_event_source)
+
+#define for_each_removed_event_source(ctx, e) \
+       for_each_helper(e, &(ctx)->removed_event_sources, struct usbi_event_source)
+
+#define for_each_removed_event_source_safe(ctx, e, n) \
+       for_each_safe_helper(e, n, &(ctx)->removed_event_sources, struct usbi_event_source)
 
 #ifdef __cplusplus
 }
index 35ea1c321e7215f13dadd329a7bcbdc4da5e9c16..e415589d670f071e6acb03247ba7b448fdb1e096 100644 (file)
@@ -1,7 +1,8 @@
 /* -*- Mode: C; indent-tabs-mode:nil -*- */
 /*
  * darwin backend for libusb 1.0
- * Copyright © 2008-2017 Nathan Hjelm <hjelmn@users.sourceforge.net>
+ * Copyright © 2008-2020 Nathan Hjelm <hjelmn@cs.unm.edu>
+ * Copyright © 2019-2020 Google LLC. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#include "config.h"
+#include <config.h>
+#include <assert.h>
 #include <time.h>
 #include <ctype.h>
-#include <errno.h>
 #include <pthread.h>
 #include <stdio.h>
 #include <stdlib.h>
   #include <objc/objc-auto.h>
 #endif
 
-#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
-/* Apple deprecated the darwin atomics in 10.12 in favor of C11 atomics */
-#include <stdatomic.h>
-#define libusb_darwin_atomic_fetch_add(x, y) atomic_fetch_add(x, y)
-
-_Atomic int32_t initCount = ATOMIC_VAR_INIT(0);
-#else
-/* use darwin atomics if the target is older than 10.12 */
-#include <libkern/OSAtomic.h>
-
-/* OSAtomicAdd32Barrier returns the new value */
-#define libusb_darwin_atomic_fetch_add(x, y) (OSAtomicAdd32Barrier(y, x) - y)
-
-static volatile int32_t initCount = 0;
-
-#endif
-
-/* On 10.12 and later, use newly available clock_*() functions */
-#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
-#define OSX_USE_CLOCK_GETTIME 1
-#else
-#define OSX_USE_CLOCK_GETTIME 0
-#endif
-
 #include "darwin_usb.h"
 
+static pthread_mutex_t libusb_darwin_init_mutex = PTHREAD_MUTEX_INITIALIZER;
+static int init_count = 0;
+
 /* async event thread */
 static pthread_mutex_t libusb_darwin_at_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_cond_t  libusb_darwin_at_cond = PTHREAD_COND_INITIALIZER;
 
-static pthread_once_t darwin_init_once = PTHREAD_ONCE_INIT;
-
-#if !OSX_USE_CLOCK_GETTIME
+#if !defined(HAVE_CLOCK_GETTIME)
 static clock_serv_t clock_realtime;
 static clock_serv_t clock_monotonic;
 #endif
 
+#define LIBUSB_DARWIN_STARTUP_FAILURE ((CFRunLoopRef) -1)
+
 static CFRunLoopRef libusb_darwin_acfl = NULL; /* event cf loop */
 static CFRunLoopSourceRef libusb_darwin_acfls = NULL; /* shutdown signal for event cf loop */
 
 static usbi_mutex_t darwin_cached_devices_lock = PTHREAD_MUTEX_INITIALIZER;
-static struct list_head darwin_cached_devices = {&darwin_cached_devices, &darwin_cached_devices};
-static const char *darwin_device_class = kIOUSBDeviceClassName;
+static struct list_head darwin_cached_devices;
+static const char *darwin_device_class = "IOUSBDevice";
 
-#define DARWIN_CACHED_DEVICE(a) ((struct darwin_cached_device *) (((struct darwin_device_priv *)((a)->os_priv))->dev))
+#define DARWIN_CACHED_DEVICE(a) (((struct darwin_device_priv *)usbi_get_device_priv((a)))->dev)
 
 /* async event thread */
 static pthread_t libusb_darwin_at;
 
-static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian);
-static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int iface);
-static int darwin_release_interface(struct libusb_device_handle *dev_handle, int iface);
+static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, void *buffer, size_t len);
+static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8_t iface);
+static int darwin_release_interface(struct libusb_device_handle *dev_handle, uint8_t iface);
 static int darwin_reset_device(struct libusb_device_handle *dev_handle);
 static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0);
 
-static int darwin_scan_devices(struct libusb_context *ctx);
-static int process_new_device (struct libusb_context *ctx, io_service_t service);
+static enum libusb_error darwin_scan_devices(struct libusb_context *ctx);
+static enum libusb_error process_new_device (struct libusb_context *ctx, struct darwin_cached_device *cached_device,
+                                             UInt64 old_session_id);
+
+static enum libusb_error darwin_get_cached_device(io_service_t service, struct darwin_cached_device **cached_out,
+                                                  UInt64 *old_session_id);
 
 #if defined(ENABLE_LOGGING)
-static const char *darwin_error_str (int result) {
+static const char *darwin_error_str (IOReturn result) {
   static char string_buffer[50];
   switch (result) {
   case kIOReturnSuccess:
@@ -137,6 +121,8 @@ static const char *darwin_error_str (int result) {
     return "out of resources";
   case kIOUSBHighSpeedSplitError:
     return "high speed split error";
+  case kIOUSBUnknownPipeErr:
+    return "pipe ref not recognized";
   default:
     snprintf(string_buffer, sizeof(string_buffer), "unknown error (0x%x)", result);
     return string_buffer;
@@ -144,7 +130,7 @@ static const char *darwin_error_str (int result) {
 }
 #endif
 
-static int darwin_to_libusb (int result) {
+static enum libusb_error darwin_to_libusb (IOReturn result) {
   switch (result) {
   case kIOReturnUnderrun:
   case kIOReturnSuccess:
@@ -164,6 +150,7 @@ static int darwin_to_libusb (int result) {
   case kIOReturnAborted:
   case kIOReturnError:
   case kIOUSBNoAsyncPortErr:
+  case kIOUSBUnknownPipeErr:
   default:
     return LIBUSB_ERROR_OTHER;
   }
@@ -176,7 +163,10 @@ static void darwin_deref_cached_device(struct darwin_cached_device *cached_dev)
   if (0 == cached_dev->refcount) {
     list_del(&cached_dev->list);
 
-    (*(cached_dev->device))->Release(cached_dev->device);
+    if (cached_dev->device) {
+      (*(cached_dev->device))->Release(cached_dev->device);
+      cached_dev->device = NULL;
+    }
     free (cached_dev);
   }
 }
@@ -186,19 +176,19 @@ static void darwin_ref_cached_device(struct darwin_cached_device *cached_dev) {
 }
 
 static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, uint8_t *pipep, uint8_t *ifcp, struct darwin_interface **interface_out) {
-  struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+  struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
 
   /* current interface */
   struct darwin_interface *cInterface;
 
-  int8_t i, iface;
+  uint8_t i, iface;
 
   usbi_dbg ("converting ep address 0x%02x to pipeRef and interface", ep);
 
   for (iface = 0 ; iface < USB_MAXINTERFACES ; iface++) {
     cInterface = &priv->interfaces[iface];
 
-    if (dev_handle->claimed_interfaces & (1 << iface)) {
+    if (dev_handle->claimed_interfaces & (1U << iface)) {
       for (i = 0 ; i < cInterface->num_endpoints ; i++) {
         if (cInterface->endpoint_addrs[i] == ep) {
           *pipep = i + 1;
@@ -210,7 +200,7 @@ static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, ui
             *interface_out = cInterface;
 
           usbi_dbg ("pipe %d on interface %d matches", *pipep, iface);
-          return 0;
+          return LIBUSB_SUCCESS;
         }
       }
     }
@@ -222,7 +212,7 @@ static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, ui
   return LIBUSB_ERROR_NOT_FOUND;
 }
 
-static int usb_setup_device_iterator (io_iterator_t *deviceIterator, UInt32 location) {
+static IOReturn usb_setup_device_iterator (io_iterator_t *deviceIterator, UInt32 location) {
   CFMutableDictionaryRef matchingDict = IOServiceMatching(darwin_device_class);
 
   if (!matchingDict)
@@ -254,24 +244,25 @@ static int usb_setup_device_iterator (io_iterator_t *deviceIterator, UInt32 loca
 }
 
 /* Returns 1 on success, 0 on failure. */
-static int get_ioregistry_value_number (io_service_t service, CFStringRef property, CFNumberType type, void *p) {
+static bool get_ioregistry_value_number (io_service_t service, CFStringRef property, CFNumberType type, void *p) {
   CFTypeRef cfNumber = IORegistryEntryCreateCFProperty (service, property, kCFAllocatorDefault, 0);
-  int ret = 0;
+  Boolean success = 0;
 
   if (cfNumber) {
     if (CFGetTypeID(cfNumber) == CFNumberGetTypeID()) {
-      ret = CFNumberGetValue(cfNumber, type, p);
+      success = CFNumberGetValue(cfNumber, type, p);
     }
 
     CFRelease (cfNumber);
   }
 
-  return ret;
+  return (success != 0);
 }
 
-static int get_ioregistry_value_data (io_service_t service, CFStringRef property, ssize_t size, void *p) {
+/* Returns 1 on success, 0 on failure. */
+static bool get_ioregistry_value_data (io_service_t service, CFStringRef property, ssize_t size, void *p) {
   CFTypeRef cfData = IORegistryEntryCreateCFProperty (service, property, kCFAllocatorDefault, 0);
-  int ret = 0;
+  bool success = false;
 
   if (cfData) {
     if (CFGetTypeID (cfData) == CFDataGetTypeID ()) {
@@ -281,28 +272,42 @@ static int get_ioregistry_value_data (io_service_t service, CFStringRef property
       }
 
       CFDataGetBytes (cfData, CFRangeMake(0, size), p);
-      ret = 1;
+      success = true;
     }
 
     CFRelease (cfData);
   }
 
-  return ret;
+  return success;
 }
 
 static usb_device_t **darwin_device_from_service (io_service_t service)
 {
   io_cf_plugin_ref_t *plugInInterface = NULL;
   usb_device_t **device;
-  kern_return_t result;
+  IOReturn kresult;
   SInt32 score;
+  const int max_retries = 5;
+
+  /* The IOCreatePlugInInterfaceForService function might consistently return
+     an "out of resources" error with certain USB devices the first time we run 
+     it. The reason is still unclear, but retrying fixes the problem */
+  for (int count = 0; count < max_retries; count++) {
+    kresult = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID,
+                                                kIOCFPlugInInterfaceID, &plugInInterface,
+                                                &score);
+    if (kIOReturnSuccess == kresult && plugInInterface) {
+      break;
+    }
 
-  result = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID,
-                                             kIOCFPlugInInterfaceID, &plugInInterface,
-                                             &score);
+    usbi_dbg ("set up plugin for service retry: %s", darwin_error_str (kresult));
 
-  if (kIOReturnSuccess != result || !plugInInterface) {
-    usbi_dbg ("could not set up plugin for service: %s", darwin_error_str (result));
+    /* sleep for a little while before trying again */
+    nanosleep(&(struct timespec){.tv_sec = 0, .tv_nsec = 1000}, NULL);
+  }
+
+  if (kIOReturnSuccess != kresult || !plugInInterface) {
+    usbi_dbg ("could not set up plugin for service: %s", darwin_error_str (kresult));
     return NULL;
   }
 
@@ -316,15 +321,28 @@ static usb_device_t **darwin_device_from_service (io_service_t service)
 
 static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) {
   UNUSED(ptr);
+  struct darwin_cached_device *cached_device;
+  UInt64 old_session_id;
   struct libusb_context *ctx;
   io_service_t service;
+  int ret;
 
   usbi_mutex_lock(&active_contexts_lock);
 
   while ((service = IOIteratorNext(add_devices))) {
+    ret = darwin_get_cached_device (service, &cached_device, &old_session_id);
+    if (ret < 0 || !cached_device->can_enumerate) {
+      continue;
+    }
+
     /* add this device to each active context's device list */
-    list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
-      process_new_device (ctx, service);
+    for_each_context(ctx) {
+      process_new_device (ctx, cached_device, old_session_id);
+    }
+
+    if (cached_device->in_reenumerate) {
+      usbi_dbg ("cached device in reset state. reset complete...");
+      cached_device->in_reenumerate = false;
     }
 
     IOObjectRelease(service);
@@ -346,6 +364,8 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) {
   usbi_mutex_lock(&active_contexts_lock);
 
   while ((device = IOIteratorNext (rem_devices)) != 0) {
+    bool is_reenumerating = false;
+
     /* get the location from the i/o registry */
     ret = get_ioregistry_value_number (device, CFSTR("sessionID"), kCFNumberSInt64Type, &session);
     IOObjectRelease (device);
@@ -357,13 +377,32 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) {
     usbi_mutex_lock(&darwin_cached_devices_lock);
     list_for_each_entry(old_device, &darwin_cached_devices, list, struct darwin_cached_device) {
       if (old_device->session == session) {
-        darwin_deref_cached_device (old_device);
+        if (old_device->in_reenumerate) {
+          /* device is re-enumerating. do not dereference the device at this time. libusb_reset_device()
+           * will deref if needed. */
+          usbi_dbg ("detected device detached due to re-enumeration");
+
+          /* the device object is no longer usable so go ahead and release it */
+          if (old_device->device) {
+            (*(old_device->device))->Release(old_device->device);
+            old_device->device = NULL;
+          }
+
+          is_reenumerating = true;
+        } else {
+          darwin_deref_cached_device (old_device);
+        }
+
         break;
       }
     }
+
     usbi_mutex_unlock(&darwin_cached_devices_lock);
+    if (is_reenumerating) {
+      continue;
+    }
 
-    list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
+    for_each_context(ctx) {
       usbi_dbg ("notifying context %p of device disconnect", ctx);
 
       dev = usbi_get_device_by_session_id(ctx, (unsigned long) session);
@@ -381,11 +420,11 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) {
 
 static void darwin_hotplug_poll (void)
 {
-  /* not sure if 5 seconds will be too long/short but it should work ok */
-  mach_timespec_t timeout = {.tv_sec = 5, .tv_nsec = 0};
+  /* not sure if 1 ms will be too long/short but it should work ok */
+  mach_timespec_t timeout = {.tv_sec = 0, .tv_nsec = 1000000ul};
 
-  /* since a kernel thread may nodify the IOInterators used for
-   * hotplug notidication we can't just clear the iterators.
+  /* since a kernel thread may notify the IOIterators used for
+   * hotplug notification we can't just clear the iterators.
    * instead just wait until all IOService providers are quiet */
   (void) IOKitWaitQuiet (kIOMasterPortDefault, &timeout);
 }
@@ -397,10 +436,20 @@ static void darwin_clear_iterator (io_iterator_t iter) {
     IOObjectRelease (device);
 }
 
+static void darwin_fail_startup(void) {
+  pthread_mutex_lock (&libusb_darwin_at_mutex);
+  libusb_darwin_acfl = LIBUSB_DARWIN_STARTUP_FAILURE;
+  pthread_cond_signal (&libusb_darwin_at_cond);
+  pthread_mutex_unlock (&libusb_darwin_at_mutex);
+  pthread_exit (NULL);
+}
+
 static void *darwin_event_thread_main (void *arg0) {
   IOReturn kresult;
   struct libusb_context *ctx = (struct libusb_context *)arg0;
   CFRunLoopRef runloop;
+  CFRunLoopSourceRef libusb_shutdown_cfsource;
+  CFRunLoopSourceContext libusb_shutdown_cfsourcectx;
 
 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
   /* Set this thread's name, so it can be seen in the debugger
@@ -419,7 +468,6 @@ static void *darwin_event_thread_main (void *arg0) {
 #endif
 
   /* hotplug (device arrival/removal) sources */
-  CFRunLoopSourceContext libusb_shutdown_cfsourcectx;
   CFRunLoopSourceRef     libusb_notification_cfsource;
   io_notification_port_t libusb_notification_port;
   io_iterator_t          libusb_rem_device_iterator;
@@ -434,8 +482,8 @@ static void *darwin_event_thread_main (void *arg0) {
   memset(&libusb_shutdown_cfsourcectx, 0, sizeof(libusb_shutdown_cfsourcectx));
   libusb_shutdown_cfsourcectx.info = runloop;
   libusb_shutdown_cfsourcectx.perform = (void (*)(void *))CFRunLoopStop;
-  libusb_darwin_acfls = CFRunLoopSourceCreate(NULL, 0, &libusb_shutdown_cfsourcectx);
-  CFRunLoopAddSource(runloop, libusb_darwin_acfls, kCFRunLoopDefaultMode);
+  libusb_shutdown_cfsource = CFRunLoopSourceCreate(NULL, 0, &libusb_shutdown_cfsourcectx);
+  CFRunLoopAddSource(runloop, libusb_shutdown_cfsource, kCFRunLoopDefaultMode);
 
   /* add the notification port to the run loop */
   libusb_notification_port     = IONotificationPortCreate (kIOMasterPortDefault);
@@ -450,8 +498,9 @@ static void *darwin_event_thread_main (void *arg0) {
 
   if (kresult != kIOReturnSuccess) {
     usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult));
-
-    pthread_exit (NULL);
+    CFRelease (libusb_shutdown_cfsource);
+    CFRelease (runloop);
+    darwin_fail_startup ();
   }
 
   /* create notifications for attached devices */
@@ -462,8 +511,9 @@ static void *darwin_event_thread_main (void *arg0) {
 
   if (kresult != kIOReturnSuccess) {
     usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult));
-
-    pthread_exit (NULL);
+    CFRelease (libusb_shutdown_cfsource);
+    CFRelease (runloop);
+    darwin_fail_startup ();
   }
 
   /* arm notifiers */
@@ -475,6 +525,7 @@ static void *darwin_event_thread_main (void *arg0) {
   /* signal the main thread that the hotplug runloop has been created. */
   pthread_mutex_lock (&libusb_darwin_at_mutex);
   libusb_darwin_acfl = runloop;
+  libusb_darwin_acfls = libusb_shutdown_cfsource;
   pthread_cond_signal (&libusb_darwin_at_cond);
   pthread_mutex_unlock (&libusb_darwin_at_mutex);
 
@@ -483,11 +534,18 @@ static void *darwin_event_thread_main (void *arg0) {
 
   usbi_dbg ("darwin event thread exiting");
 
+  /* signal the main thread that the hotplug runloop has finished. */
+  pthread_mutex_lock (&libusb_darwin_at_mutex);
+  libusb_darwin_acfls = NULL;
+  libusb_darwin_acfl = NULL;
+  pthread_cond_signal (&libusb_darwin_at_cond);
+  pthread_mutex_unlock (&libusb_darwin_at_mutex);
+
   /* remove the notification cfsource */
   CFRunLoopRemoveSource(runloop, libusb_notification_cfsource, kCFRunLoopDefaultMode);
 
   /* remove the shutdown cfsource */
-  CFRunLoopRemoveSource(runloop, libusb_darwin_acfls, kCFRunLoopDefaultMode);
+  CFRunLoopRemoveSource(runloop, libusb_shutdown_cfsource, kCFRunLoopDefaultMode);
 
   /* delete notification port */
   IONotificationPortDestroy (libusb_notification_port);
@@ -496,104 +554,116 @@ static void *darwin_event_thread_main (void *arg0) {
   IOObjectRelease (libusb_rem_device_iterator);
   IOObjectRelease (libusb_add_device_iterator);
 
-  CFRelease (libusb_darwin_acfls);
+  CFRelease (libusb_shutdown_cfsource);
   CFRelease (runloop);
 
-  libusb_darwin_acfls = NULL;
-  libusb_darwin_acfl = NULL;
-
   pthread_exit (NULL);
 }
 
 /* cleanup function to destroy cached devices */
-static void __attribute__((destructor)) _darwin_finalize(void) {
+static void darwin_cleanup_devices(void) {
   struct darwin_cached_device *dev, *next;
 
-  usbi_mutex_lock(&darwin_cached_devices_lock);
   list_for_each_entry_safe(dev, next, &darwin_cached_devices, list, struct darwin_cached_device) {
     darwin_deref_cached_device(dev);
   }
-  usbi_mutex_unlock(&darwin_cached_devices_lock);
-}
-
-static void darwin_check_version (void) {
-  /* adjust for changes in the USB stack in xnu 15 */
-  int sysctl_args[] = {CTL_KERN, KERN_OSRELEASE};
-  long version;
-  char version_string[256] = {'\0',};
-  size_t length = 256;
 
-  sysctl(sysctl_args, 2, version_string, &length, NULL, 0);
-
-  errno = 0;
-  version = strtol (version_string, NULL, 10);
-  if (0 == errno && version >= 15) {
-    darwin_device_class = "IOUSBHostDevice";
-  }
+  darwin_cached_devices.prev = darwin_cached_devices.next = NULL;
 }
 
 static int darwin_init(struct libusb_context *ctx) {
+  bool first_init;
   int rc;
 
-  rc = pthread_once (&darwin_init_once, darwin_check_version);
-  if (rc) {
-    return LIBUSB_ERROR_OTHER;
-  }
-
-  rc = darwin_scan_devices (ctx);
-  if (LIBUSB_SUCCESS != rc) {
-    return rc;
-  }
+  pthread_mutex_lock (&libusb_darwin_init_mutex);
 
-  if (libusb_darwin_atomic_fetch_add (&initCount, 1) == 0) {
-#if !OSX_USE_CLOCK_GETTIME
-    /* create the clocks that will be used if clock_gettime() is not available */
-    host_name_port_t host_self;
+  first_init = (1 == ++init_count);
 
-    host_self = mach_host_self();
-    host_get_clock_service(host_self, CALENDAR_CLOCK, &clock_realtime);
-    host_get_clock_service(host_self, SYSTEM_CLOCK, &clock_monotonic);
-    mach_port_deallocate(mach_task_self(), host_self);
+  do {
+    if (first_init) {
+      assert (NULL == darwin_cached_devices.next);
+      list_init (&darwin_cached_devices);
+
+#if !defined(HAVE_CLOCK_GETTIME)
+      /* create the clocks that will be used if clock_gettime() is not available */
+      host_name_port_t host_self;
+
+      host_self = mach_host_self();
+      host_get_clock_service(host_self, CALENDAR_CLOCK, &clock_realtime);
+      host_get_clock_service(host_self, SYSTEM_CLOCK, &clock_monotonic);
+      mach_port_deallocate(mach_task_self(), host_self);
 #endif
+    }
 
-    pthread_create (&libusb_darwin_at, NULL, darwin_event_thread_main, ctx);
+    rc = darwin_scan_devices (ctx);
+    if (LIBUSB_SUCCESS != rc)
+      break;
 
-    pthread_mutex_lock (&libusb_darwin_at_mutex);
-    while (!libusb_darwin_acfl)
-      pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex);
-    pthread_mutex_unlock (&libusb_darwin_at_mutex);
+    if (first_init) {
+      rc = pthread_create (&libusb_darwin_at, NULL, darwin_event_thread_main, ctx);
+      if (0 != rc) {
+        usbi_err (ctx, "could not create event thread, error %d", rc);
+        rc = LIBUSB_ERROR_OTHER;
+        break;
+      }
+
+      pthread_mutex_lock (&libusb_darwin_at_mutex);
+      while (!libusb_darwin_acfl)
+        pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex);
+      if (libusb_darwin_acfl == LIBUSB_DARWIN_STARTUP_FAILURE) {
+        libusb_darwin_acfl = NULL;
+        rc = LIBUSB_ERROR_OTHER;
+      }
+      pthread_mutex_unlock (&libusb_darwin_at_mutex);
+
+      if (0 != rc)
+        pthread_join (libusb_darwin_at, NULL);
+    }
+  } while (0);
+
+  if (LIBUSB_SUCCESS != rc) {
+    if (first_init) {
+      darwin_cleanup_devices ();
+#if !defined(HAVE_CLOCK_GETTIME)
+      mach_port_deallocate(mach_task_self(), clock_realtime);
+      mach_port_deallocate(mach_task_self(), clock_monotonic);
+#endif
+    }
+    --init_count;
   }
 
+  pthread_mutex_unlock (&libusb_darwin_init_mutex);
+
   return rc;
 }
 
 static void darwin_exit (struct libusb_context *ctx) {
   UNUSED(ctx);
-  if (libusb_darwin_atomic_fetch_add (&initCount, -1) == 1) {
-#if !OSX_USE_CLOCK_GETTIME
-    mach_port_deallocate(mach_task_self(), clock_realtime);
-    mach_port_deallocate(mach_task_self(), clock_monotonic);
-#endif
 
+  pthread_mutex_lock (&libusb_darwin_init_mutex);
+
+  if (0 == --init_count) {
     /* stop the event runloop and wait for the thread to terminate. */
-    CFRunLoopSourceSignal(libusb_darwin_acfls);
+    pthread_mutex_lock (&libusb_darwin_at_mutex);
+    CFRunLoopSourceSignal (libusb_darwin_acfls);
     CFRunLoopWakeUp (libusb_darwin_acfl);
+    while (libusb_darwin_acfl)
+      pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex);
+    pthread_mutex_unlock (&libusb_darwin_at_mutex);
     pthread_join (libusb_darwin_at, NULL);
-  }
-}
 
-static int darwin_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer, int *host_endian) {
-  struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev);
-
-  /* return cached copy */
-  memmove (buffer, &(priv->dev_descriptor), DEVICE_DESC_LENGTH);
+    darwin_cleanup_devices ();
 
-  *host_endian = 0;
+#if !defined(HAVE_CLOCK_GETTIME)
+    mach_port_deallocate(mach_task_self(), clock_realtime);
+    mach_port_deallocate(mach_task_self(), clock_monotonic);
+#endif
+  }
 
-  return 0;
+  pthread_mutex_unlock (&libusb_darwin_init_mutex);
 }
 
-static int get_configuration_index (struct libusb_device *dev, int config_value) {
+static int get_configuration_index (struct libusb_device *dev, UInt8 config_value) {
   struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev);
   UInt8 i, numConfig;
   IOUSBConfigurationDescriptorPtr desc;
@@ -615,7 +685,7 @@ static int get_configuration_index (struct libusb_device *dev, int config_value)
   return LIBUSB_ERROR_NOT_FOUND;
 }
 
-static int darwin_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian) {
+static int darwin_get_active_config_descriptor(struct libusb_device *dev, void *buffer, size_t len) {
   struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev);
   int config_index;
 
@@ -626,10 +696,11 @@ static int darwin_get_active_config_descriptor(struct libusb_device *dev, unsign
   if (config_index < 0)
     return config_index;
 
-  return darwin_get_config_descriptor (dev, config_index, buffer, len, host_endian);
+  assert(config_index >= 0 && config_index <= UINT8_MAX);
+  return darwin_get_config_descriptor (dev, (UInt8)config_index, buffer, len);
 }
 
-static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian) {
+static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, void *buffer, size_t len) {
   struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev);
   IOUSBConfigurationDescriptorPtr desc;
   IOReturn kresult;
@@ -645,9 +716,6 @@ static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t confi
       len = libusb_le16_to_cpu(desc->wTotalLength);
 
     memmove (buffer, desc, len);
-
-    /* GetConfigurationDescriptorPtr returns the descriptor in USB bus order */
-    *host_endian = 0;
   }
 
   ret = darwin_to_libusb (kresult);
@@ -658,12 +726,12 @@ static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t confi
 }
 
 /* check whether the os has configured the device */
-static int darwin_check_configuration (struct libusb_context *ctx, struct darwin_cached_device *dev) {
+static enum libusb_error darwin_check_configuration (struct libusb_context *ctx, struct darwin_cached_device *dev) {
   usb_device_t **darwin_device = dev->device;
 
   IOUSBConfigurationDescriptorPtr configDesc;
   IOUSBFindInterfaceRequest request;
-  kern_return_t             kresult;
+  IOReturn                  kresult;
   io_iterator_t             interface_iterator;
   io_service_t              firstInterface;
 
@@ -674,10 +742,11 @@ static int darwin_check_configuration (struct libusb_context *ctx, struct darwin
 
   /* checking the configuration of a root hub simulation takes ~1 s in 10.11. the device is
      not usable anyway */
-  if (0x05ac == dev->dev_descriptor.idVendor && 0x8005 == dev->dev_descriptor.idProduct) {
+  if (0x05ac == libusb_le16_to_cpu (dev->dev_descriptor.idVendor) &&
+      0x8005 == libusb_le16_to_cpu (dev->dev_descriptor.idProduct)) {
     usbi_dbg ("ignoring configuration on root hub simulation");
     dev->active_config = 0;
-    return 0;
+    return LIBUSB_SUCCESS;
   }
 
   /* find the first configuration */
@@ -695,7 +764,7 @@ static int darwin_check_configuration (struct libusb_context *ctx, struct darwin
   request.bAlternateSetting  = kIOUSBFindInterfaceDontCare;
 
   kresult = (*(darwin_device))->CreateInterfaceIterator(darwin_device, &request, &interface_iterator);
-  if (kresult)
+  if (kresult != kIOReturnSuccess)
     return darwin_to_libusb (kresult);
 
   /* iterate once */
@@ -717,23 +786,25 @@ static int darwin_check_configuration (struct libusb_context *ctx, struct darwin
   } else
     /* not configured */
     dev->active_config = 0;
-  
+
   usbi_dbg ("active config: %u, first config: %u", dev->active_config, dev->first_config);
 
-  return 0;
+  return LIBUSB_SUCCESS;
 }
 
-static int darwin_request_descriptor (usb_device_t **device, UInt8 desc, UInt8 desc_index, void *buffer, size_t buffer_size) {
+static IOReturn darwin_request_descriptor (usb_device_t **device, UInt8 desc, UInt8 desc_index, void *buffer, size_t buffer_size) {
   IOUSBDevRequestTO req;
 
+  assert(buffer_size <= UINT16_MAX);
+
   memset (buffer, 0, buffer_size);
 
   /* Set up request for descriptor/ */
   req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
   req.bRequest      = kUSBRqGetDescriptor;
-  req.wValue        = desc << 8;
+  req.wValue        = (UInt16)(desc << 8);
   req.wIndex        = desc_index;
-  req.wLength       = buffer_size;
+  req.wLength       = (UInt16)buffer_size;
   req.pData         = buffer;
   req.noDataTimeout = 20;
   req.completionTimeout = 100;
@@ -741,12 +812,13 @@ static int darwin_request_descriptor (usb_device_t **device, UInt8 desc, UInt8 d
   return (*device)->DeviceRequestTO (device, &req);
 }
 
-static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct darwin_cached_device *dev) {
+static enum libusb_error darwin_cache_device_descriptor (struct darwin_cached_device *dev) {
   usb_device_t **device = dev->device;
-  int retries = 1, delay = 30000;
+  int retries = 1;
+  long delay = 30000; // microseconds
   int unsuspended = 0, try_unsuspend = 1, try_reconfigure = 1;
   int is_open = 0;
-  int ret = 0, ret2;
+  IOReturn ret = 0, ret2;
   UInt8 bDeviceClass;
   UInt16 idProduct, idVendor;
 
@@ -799,7 +871,7 @@ static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct da
       (void)(*device)->GetUSBDeviceInformation (device, &info);
 
       /* note that the device was suspended */
-      if (info & (1 << kUSBInformationDeviceIsSuspendedBit) || 0 == info)
+      if (info & (1U << kUSBInformationDeviceIsSuspendedBit) || 0 == info)
         try_unsuspend = 1;
 #endif
 
@@ -818,9 +890,9 @@ static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct da
     }
 
     if (kIOReturnSuccess != ret) {
-      usbi_dbg("kernel responded with code: 0x%08x. sleeping for %d ms before trying again", ret, delay/1000);
+      usbi_dbg("kernel responded with code: 0x%08x. sleeping for %ld ms before trying again", ret, delay/1000);
       /* sleep for a little while before trying again */
-      nanosleep(&(struct timespec){delay / 1000000, (delay * 1000) % 1000000000UL}, NULL);
+      nanosleep(&(struct timespec){delay / 1000000, (delay * 1000) % 1000000000}, NULL);
     }
   } while (kIOReturnSuccess != ret && retries--);
 
@@ -837,7 +909,7 @@ static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct da
       usbi_dbg ("could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
                 idVendor, idProduct, darwin_error_str (ret), ret);
     else
-      usbi_warn (ctx, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
+      usbi_warn (NULL, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device",
                  idVendor, idProduct, darwin_error_str (ret), ret);
     return darwin_to_libusb (ret);
   }
@@ -845,21 +917,21 @@ static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct da
   /* catch buggy hubs (which appear to be virtual). Apple's own USB prober has problems with these devices. */
   if (libusb_le16_to_cpu (dev->dev_descriptor.idProduct) != idProduct) {
     /* not a valid device */
-    usbi_warn (ctx, "idProduct from iokit (%04x) does not match idProduct in descriptor (%04x). skipping device",
+    usbi_warn (NULL, "idProduct from iokit (%04x) does not match idProduct in descriptor (%04x). skipping device",
                idProduct, libusb_le16_to_cpu (dev->dev_descriptor.idProduct));
     return LIBUSB_ERROR_NO_DEVICE;
   }
 
   usbi_dbg ("cached device descriptor:");
   usbi_dbg ("  bDescriptorType:    0x%02x", dev->dev_descriptor.bDescriptorType);
-  usbi_dbg ("  bcdUSB:             0x%04x", dev->dev_descriptor.bcdUSB);
+  usbi_dbg ("  bcdUSB:             0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.bcdUSB));
   usbi_dbg ("  bDeviceClass:       0x%02x", dev->dev_descriptor.bDeviceClass);
   usbi_dbg ("  bDeviceSubClass:    0x%02x", dev->dev_descriptor.bDeviceSubClass);
   usbi_dbg ("  bDeviceProtocol:    0x%02x", dev->dev_descriptor.bDeviceProtocol);
   usbi_dbg ("  bMaxPacketSize0:    0x%02x", dev->dev_descriptor.bMaxPacketSize0);
-  usbi_dbg ("  idVendor:           0x%04x", dev->dev_descriptor.idVendor);
-  usbi_dbg ("  idProduct:          0x%04x", dev->dev_descriptor.idProduct);
-  usbi_dbg ("  bcdDevice:          0x%04x", dev->dev_descriptor.bcdDevice);
+  usbi_dbg ("  idVendor:           0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.idVendor));
+  usbi_dbg ("  idProduct:          0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.idProduct));
+  usbi_dbg ("  bcdDevice:          0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.bcdDevice));
   usbi_dbg ("  iManufacturer:      0x%02x", dev->dev_descriptor.iManufacturer);
   usbi_dbg ("  iProduct:           0x%02x", dev->dev_descriptor.iProduct);
   usbi_dbg ("  iSerialNumber:      0x%02x", dev->dev_descriptor.iSerialNumber);
@@ -870,17 +942,18 @@ static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct da
   return LIBUSB_SUCCESS;
 }
 
-static int get_device_port (io_service_t service, UInt8 *port) {
-  kern_return_t result;
+/* Returns 1 on success, 0 on failure. */
+static bool get_device_port (io_service_t service, UInt8 *port) {
+  IOReturn kresult;
   io_service_t parent;
-  int ret = 0;
+  bool ret = false;
 
   if (get_ioregistry_value_number (service, CFSTR("PortNum"), kCFNumberSInt8Type, port)) {
-    return 1;
+    return true;
   }
 
-  result = IORegistryEntryGetParentEntry (service, kIOServicePlane, &parent);
-  if (kIOReturnSuccess == result) {
+  kresult = IORegistryEntryGetParentEntry (service, kIOServicePlane, &parent);
+  if (kIOReturnSuccess == kresult) {
     ret = get_ioregistry_value_data (parent, CFSTR("port"), 1, port);
     IOObjectRelease (parent);
   }
@@ -888,33 +961,40 @@ static int get_device_port (io_service_t service, UInt8 *port) {
   return ret;
 }
 
-static int get_device_parent_sessionID(io_service_t service, UInt64 *parent_sessionID) {
-  kern_return_t result;
+/* Returns 1 on success, 0 on failure. */
+static bool get_device_parent_sessionID(io_service_t service, UInt64 *parent_sessionID) {
+  IOReturn kresult;
   io_service_t parent;
 
   /* Walk up the tree in the IOService plane until we find a parent that has a sessionID */
   parent = service;
-  while((result = IORegistryEntryGetParentEntry (parent, kIOServicePlane, &parent)) == kIOReturnSuccess) {
+  while((kresult = IORegistryEntryGetParentEntry (parent, kIOUSBPlane, &parent)) == kIOReturnSuccess) {
     if (get_ioregistry_value_number (parent, CFSTR("sessionID"), kCFNumberSInt64Type, parent_sessionID)) {
         /* Success */
-        return 1;
+        return true;
     }
   }
 
   /* We ran out of parents */
-  return 0;
+  return false;
 }
 
-static int darwin_get_cached_device(struct libusb_context *ctx, io_service_t service,
-                                    struct darwin_cached_device **cached_out) {
+static enum libusb_error darwin_get_cached_device(io_service_t service, struct darwin_cached_device **cached_out,
+                                                  UInt64 *old_session_id) {
   struct darwin_cached_device *new_device;
   UInt64 sessionID = 0, parent_sessionID = 0;
-  int ret = LIBUSB_SUCCESS;
+  UInt32 locationID = 0;
+  enum libusb_error ret = LIBUSB_SUCCESS;
   usb_device_t **device;
   UInt8 port = 0;
 
+  /* assuming sessionID != 0 normally (never seen it be 0) */
+  *old_session_id = 0;
+  *cached_out = NULL;
+
   /* get some info from the io registry */
   (void) get_ioregistry_value_number (service, CFSTR("sessionID"), kCFNumberSInt64Type, &sessionID);
+  (void) get_ioregistry_value_number (service, CFSTR("locationID"), kCFNumberSInt32Type, &locationID);
   if (!get_device_port (service, &port)) {
     usbi_dbg("could not get connected port number");
   }
@@ -927,10 +1007,15 @@ static int darwin_get_cached_device(struct libusb_context *ctx, io_service_t ser
 
   usbi_mutex_lock(&darwin_cached_devices_lock);
   do {
-    *cached_out = NULL;
-
     list_for_each_entry(new_device, &darwin_cached_devices, list, struct darwin_cached_device) {
-      usbi_dbg("matching sessionID 0x%" PRIx64 " against cached device with sessionID 0x%" PRIx64, sessionID, new_device->session);
+      usbi_dbg("matching sessionID/locationID 0x%" PRIx64 "/0x%x against cached device with sessionID/locationID 0x%" PRIx64 "/0x%x",
+               sessionID, locationID, new_device->session, new_device->location);
+      if (new_device->location == locationID && new_device->in_reenumerate) {
+        usbi_dbg ("found cached device with matching location that is being re-enumerated");
+        *old_session_id = new_device->session;
+        break;
+      }
+
       if (new_device->session == sessionID) {
         usbi_dbg("using cached device for device");
         *cached_out = new_device;
@@ -949,89 +1034,104 @@ static int darwin_get_cached_device(struct libusb_context *ctx, io_service_t ser
       break;
     }
 
-    new_device = calloc (1, sizeof (*new_device));
-    if (!new_device) {
-      ret = LIBUSB_ERROR_NO_MEM;
-      break;
-    }
+    if (!(*old_session_id)) {
+      new_device = calloc (1, sizeof (*new_device));
+      if (!new_device) {
+        ret = LIBUSB_ERROR_NO_MEM;
+        break;
+      }
 
-    /* add this device to the cached device list */
-    list_add(&new_device->list, &darwin_cached_devices);
+      /* add this device to the cached device list */
+      list_add(&new_device->list, &darwin_cached_devices);
 
-    (*device)->GetDeviceAddress (device, (USBDeviceAddress *)&new_device->address);
+      (*device)->GetDeviceAddress (device, (USBDeviceAddress *)&new_device->address);
 
-    /* keep a reference to this device */
-    darwin_ref_cached_device(new_device);
+      /* keep a reference to this device */
+      darwin_ref_cached_device(new_device);
+
+      (*device)->GetLocationID (device, &new_device->location);
+      new_device->port = port;
+      new_device->parent_session = parent_sessionID;
+    }
+
+    /* keep track of devices regardless of if we successfully enumerate them to
+       prevent them from being enumerated multiple times */
+    *cached_out = new_device;
 
-    new_device->device = device;
     new_device->session = sessionID;
-    (*device)->GetLocationID (device, &new_device->location);
-    new_device->port = port;
-    new_device->parent_session = parent_sessionID;
+    new_device->device = device;
 
     /* cache the device descriptor */
-    ret = darwin_cache_device_descriptor(ctx, new_device);
+    ret = darwin_cache_device_descriptor(new_device);
     if (ret)
       break;
 
     if (new_device->can_enumerate) {
       snprintf(new_device->sys_path, 20, "%03i-%04x-%04x-%02x-%02x", new_device->address,
-               new_device->dev_descriptor.idVendor, new_device->dev_descriptor.idProduct,
+               libusb_le16_to_cpu (new_device->dev_descriptor.idVendor),
+               libusb_le16_to_cpu (new_device->dev_descriptor.idProduct),
                new_device->dev_descriptor.bDeviceClass, new_device->dev_descriptor.bDeviceSubClass);
     }
   } while (0);
 
   usbi_mutex_unlock(&darwin_cached_devices_lock);
 
-  /* keep track of devices regardless of if we successfully enumerate them to
-     prevent them from being enumerated multiple times */
-
-  *cached_out = new_device;
-
   return ret;
 }
 
-static int process_new_device (struct libusb_context *ctx, io_service_t service) {
+static enum libusb_error process_new_device (struct libusb_context *ctx, struct darwin_cached_device *cached_device,
+                                             UInt64 old_session_id) {
   struct darwin_device_priv *priv;
   struct libusb_device *dev = NULL;
-  struct darwin_cached_device *cached_device;
   UInt8 devSpeed;
-  int ret = 0;
+  enum libusb_error ret = LIBUSB_SUCCESS;
 
   do {
-    ret = darwin_get_cached_device (ctx, service, &cached_device);
-
-    if (ret < 0 || !cached_device->can_enumerate) {
-      return ret;
-    }
-
     /* check current active configuration (and cache the first configuration value--
        which may be used by claim_interface) */
     ret = darwin_check_configuration (ctx, cached_device);
     if (ret)
       break;
 
-    usbi_dbg ("allocating new device in context %p for with session 0x%" PRIx64,
-              ctx, cached_device->session);
+    if (0 != old_session_id) {
+      usbi_dbg ("re-using existing device from context %p for with session 0x%" PRIx64 " new session 0x%" PRIx64,
+                ctx, old_session_id, cached_device->session);
+      /* save the libusb device before the session id is updated */
+      dev = usbi_get_device_by_session_id (ctx, (unsigned long) old_session_id);
+    }
 
-    dev = usbi_alloc_device(ctx, (unsigned long) cached_device->session);
     if (!dev) {
-      return LIBUSB_ERROR_NO_MEM;
-    }
+      usbi_dbg ("allocating new device in context %p for with session 0x%" PRIx64,
+                ctx, cached_device->session);
 
-    priv = (struct darwin_device_priv *)dev->os_priv;
+      dev = usbi_alloc_device(ctx, (unsigned long) cached_device->session);
+      if (!dev) {
+        return LIBUSB_ERROR_NO_MEM;
+      }
 
-    priv->dev = cached_device;
-    darwin_ref_cached_device (priv->dev);
+      priv = usbi_get_device_priv(dev);
+
+      priv->dev = cached_device;
+      darwin_ref_cached_device (priv->dev);
+      dev->port_number    = cached_device->port;
+      dev->bus_number     = cached_device->location >> 24;
+      assert(cached_device->address <= UINT8_MAX);
+      dev->device_address = (uint8_t)cached_device->address;
+    } else {
+      priv = usbi_get_device_priv(dev);
+    }
+
+    static_assert(sizeof(dev->device_descriptor) == sizeof(cached_device->dev_descriptor),
+                  "mismatch between libusb and IOKit device descriptor sizes");
+    memcpy(&dev->device_descriptor, &cached_device->dev_descriptor, LIBUSB_DT_DEVICE_SIZE);
+    usbi_localize_device_descriptor(&dev->device_descriptor);
+    dev->session_data = cached_device->session;
 
     if (cached_device->parent_session > 0) {
       dev->parent_dev = usbi_get_device_by_session_id (ctx, (unsigned long) cached_device->parent_session);
     } else {
       dev->parent_dev = NULL;
     }
-    dev->port_number    = cached_device->port;
-    dev->bus_number     = cached_device->location >> 24;
-    dev->device_address = cached_device->address;
 
     (*(priv->dev->device))->GetDeviceSpeed (priv->dev->device, &devSpeed);
 
@@ -1055,9 +1155,10 @@ static int process_new_device (struct libusb_context *ctx, io_service_t service)
 
     usbi_dbg ("found device with address %d port = %d parent = %p at %p", dev->device_address,
               dev->port_number, (void *) dev->parent_dev, priv->dev->sys_path);
+
   } while (0);
 
-  if (0 == ret) {
+  if (!cached_device->in_reenumerate && 0 == ret) {
     usbi_connect_device (dev);
   } else {
     libusb_unref_device (dev);
@@ -1066,28 +1167,36 @@ static int process_new_device (struct libusb_context *ctx, io_service_t service)
   return ret;
 }
 
-static int darwin_scan_devices(struct libusb_context *ctx) {
+static enum libusb_error darwin_scan_devices(struct libusb_context *ctx) {
+  struct darwin_cached_device *cached_device;
+  UInt64 old_session_id;
   io_iterator_t deviceIterator;
   io_service_t service;
-  kern_return_t kresult;
+  IOReturn kresult;
+  int ret;
 
   kresult = usb_setup_device_iterator (&deviceIterator, 0);
   if (kresult != kIOReturnSuccess)
     return darwin_to_libusb (kresult);
 
   while ((service = IOIteratorNext (deviceIterator))) {
-    (void) process_new_device (ctx, service);
+    ret = darwin_get_cached_device (service, &cached_device, &old_session_id);
+    if (ret < 0 || !cached_device->can_enumerate) {
+      continue;
+    }
+
+    (void) process_new_device (ctx, cached_device, old_session_id);
 
     IOObjectRelease(service);
   }
 
   IOObjectRelease(deviceIterator);
 
-  return 0;
+  return LIBUSB_SUCCESS;
 }
 
 static int darwin_open (struct libusb_device_handle *dev_handle) {
-  struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+  struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
   struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
   IOReturn kresult;
 
@@ -1102,9 +1211,9 @@ static int darwin_open (struct libusb_device_handle *dev_handle) {
       }
 
       /* it is possible to perform some actions on a device that is not open so do not return an error */
-      priv->is_open = 0;
+      priv->is_open = false;
     } else {
-      priv->is_open = 1;
+      priv->is_open = true;
     }
 
     /* create async event source */
@@ -1116,7 +1225,7 @@ static int darwin_open (struct libusb_device_handle *dev_handle) {
         (*(dpriv->device))->USBDeviceClose (dpriv->device);
       }
 
-      priv->is_open = 0;
+      priv->is_open = false;
 
       return darwin_to_libusb (kresult);
     }
@@ -1136,7 +1245,7 @@ static int darwin_open (struct libusb_device_handle *dev_handle) {
 }
 
 static void darwin_close (struct libusb_device_handle *dev_handle) {
-  struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+  struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
   struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
   IOReturn kresult;
   int i;
@@ -1151,7 +1260,7 @@ static void darwin_close (struct libusb_device_handle *dev_handle) {
 
   /* make sure all interfaces are released */
   for (i = 0 ; i < USB_MAXINTERFACES ; i++)
-    if (dev_handle->claimed_interfaces & (1 << i))
+    if (dev_handle->claimed_interfaces & (1U << i))
       libusb_release_interface (dev_handle, i);
 
   if (0 == dpriv->open_count) {
@@ -1166,7 +1275,7 @@ static void darwin_close (struct libusb_device_handle *dev_handle) {
     if (priv->is_open) {
       /* close the device */
       kresult = (*(dpriv->device))->USBDeviceClose(dpriv->device);
-      if (kresult) {
+      if (kresult != kIOReturnSuccess) {
         /* Log the fact that we had a problem closing the file, however failing a
          * close isn't really an error, so return success anyway */
         usbi_warn (HANDLE_CTX (dev_handle), "USBDeviceClose: %s", darwin_error_str(kresult));
@@ -1175,45 +1284,48 @@ static void darwin_close (struct libusb_device_handle *dev_handle) {
   }
 }
 
-static int darwin_get_configuration(struct libusb_device_handle *dev_handle, int *config) {
+static int darwin_get_configuration(struct libusb_device_handle *dev_handle, uint8_t *config) {
   struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
 
-  *config = (int) dpriv->active_config;
+  *config = dpriv->active_config;
 
-  return 0;
+  return LIBUSB_SUCCESS;
 }
 
-static int darwin_set_configuration(struct libusb_device_handle *dev_handle, int config) {
+static enum libusb_error darwin_set_configuration(struct libusb_device_handle *dev_handle, int config) {
   struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
   IOReturn kresult;
-  int i;
+  uint8_t i;
+
+  if (config == -1)
+    config = 0;
 
   /* Setting configuration will invalidate the interface, so we need
      to reclaim it. First, dispose of existing interfaces, if any. */
   for (i = 0 ; i < USB_MAXINTERFACES ; i++)
-    if (dev_handle->claimed_interfaces & (1 << i))
+    if (dev_handle->claimed_interfaces & (1U << i))
       darwin_release_interface (dev_handle, i);
 
-  kresult = (*(dpriv->device))->SetConfiguration (dpriv->device, config);
+  kresult = (*(dpriv->device))->SetConfiguration (dpriv->device, (UInt8)config);
   if (kresult != kIOReturnSuccess)
     return darwin_to_libusb (kresult);
 
   /* Reclaim any interfaces. */
   for (i = 0 ; i < USB_MAXINTERFACES ; i++)
-    if (dev_handle->claimed_interfaces & (1 << i))
+    if (dev_handle->claimed_interfaces & (1U << i))
       darwin_claim_interface (dev_handle, i);
 
-  dpriv->active_config = config;
+  dpriv->active_config = (UInt8)config;
 
-  return 0;
+  return LIBUSB_SUCCESS;
 }
 
-static int darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_service_t *usbInterfacep) {
+static IOReturn darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_service_t *usbInterfacep) {
   IOUSBFindInterfaceRequest request;
-  kern_return_t             kresult;
+  IOReturn                  kresult;
   io_iterator_t             interface_iterator;
   UInt8                     bInterfaceNumber;
-  int                       ret;
+  bool                      ret;
 
   *usbInterfacep = IO_OBJECT_NULL;
 
@@ -1224,7 +1336,7 @@ static int darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_s
   request.bAlternateSetting  = kIOUSBFindInterfaceDontCare;
 
   kresult = (*(darwin_device))->CreateInterfaceIterator(darwin_device, &request, &interface_iterator);
-  if (kresult)
+  if (kresult != kIOReturnSuccess)
     return kresult;
 
   while ((*usbInterfacep = IOIteratorNext(interface_iterator))) {
@@ -1242,16 +1354,16 @@ static int darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_s
   /* done with the interface iterator */
   IOObjectRelease(interface_iterator);
 
-  return 0;
+  return kIOReturnSuccess;
 }
 
-static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) {
-  struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+static enum libusb_error get_endpoints (struct libusb_device_handle *dev_handle, uint8_t iface) {
+  struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
 
   /* current interface */
   struct darwin_interface *cInterface = &priv->interfaces[iface];
 
-  kern_return_t kresult;
+  IOReturn kresult;
 
   UInt8 numep, direction, number;
   UInt8 dont_care1, dont_care3;
@@ -1262,13 +1374,13 @@ static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) {
 
   /* retrieve the total number of endpoints on this interface */
   kresult = (*(cInterface->interface))->GetNumEndpoints(cInterface->interface, &numep);
-  if (kresult) {
+  if (kresult != kIOReturnSuccess) {
     usbi_err (HANDLE_CTX (dev_handle), "can't get number of endpoints for interface: %s", darwin_error_str(kresult));
     return darwin_to_libusb (kresult);
   }
 
   /* iterate through pipe references */
-  for (int i = 1 ; i <= numep ; i++) {
+  for (UInt8 i = 1 ; i <= numep ; i++) {
     kresult = (*(cInterface->interface))->GetPipeProperties(cInterface->interface, i, &direction, &number, &dont_care1,
                                                             &dont_care2, &dont_care3);
 
@@ -1279,7 +1391,7 @@ static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) {
       UInt8 alt_setting;
 
       kresult = (*(cInterface->interface))->GetAlternateSetting (cInterface->interface, &alt_setting);
-      if (kresult) {
+      if (kresult != kIOReturnSuccess) {
         usbi_err (HANDLE_CTX (dev_handle), "can't get alternate setting for interface");
         return darwin_to_libusb (kresult);
       }
@@ -1293,7 +1405,7 @@ static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) {
 
       cInterface->endpoint_addrs[i - 1] = endpoint_desc->bEndpointAddress;
     } else {
-      cInterface->endpoint_addrs[i - 1] = (((kUSBIn == direction) << kUSBRqDirnShift) | (number & LIBUSB_ENDPOINT_ADDRESS_MASK));
+      cInterface->endpoint_addrs[i - 1] = (UInt8)(((kUSBIn == direction) << kUSBRqDirnShift) | (number & LIBUSB_ENDPOINT_ADDRESS_MASK));
     }
 
     usbi_dbg ("interface: %i pipe %i: dir: %i number: %i", iface, i, cInterface->endpoint_addrs[i - 1] >> kUSBRqDirnShift,
@@ -1302,14 +1414,15 @@ static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) {
 
   cInterface->num_endpoints = numep;
 
-  return 0;
+  return LIBUSB_SUCCESS;
 }
 
-static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int iface) {
+static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8_t iface) {
   struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
-  struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+  struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
   io_service_t          usbInterface = IO_OBJECT_NULL;
-  IOReturn kresult;
+  IOReturn              kresult;
+  enum libusb_error     ret;
   IOCFPlugInInterface **plugInInterface = NULL;
   SInt32                score;
 
@@ -1325,14 +1438,14 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int i
     usbi_info (HANDLE_CTX (dev_handle), "no interface found; setting configuration: %d", dpriv->first_config);
 
     /* set the configuration */
-    kresult = darwin_set_configuration (dev_handle, dpriv->first_config);
-    if (kresult != LIBUSB_SUCCESS) {
+    ret = darwin_set_configuration (dev_handle, (int) dpriv->first_config);
+    if (ret != LIBUSB_SUCCESS) {
       usbi_err (HANDLE_CTX (dev_handle), "could not set configuration");
-      return kresult;
+      return ret;
     }
 
     kresult = darwin_get_interface (dpriv->device, iface, &usbInterface);
-    if (kresult) {
+    if (kresult != kIOReturnSuccess) {
       usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult));
       return darwin_to_libusb (kresult);
     }
@@ -1350,7 +1463,7 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int i
   /* ignore release error */
   (void)IOObjectRelease (usbInterface);
 
-  if (kresult) {
+  if (kresult != kIOReturnSuccess) {
     usbi_err (HANDLE_CTX (dev_handle), "IOCreatePlugInInterfaceForService: %s", darwin_error_str(kresult));
     return darwin_to_libusb (kresult);
   }
@@ -1362,30 +1475,30 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int i
 
   /* Do the actual claim */
   kresult = (*plugInInterface)->QueryInterface(plugInInterface,
-                                               CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
+                                               CFUUIDGetUUIDBytes(InterfaceInterfaceID),
                                                (LPVOID)&cInterface->interface);
   /* We no longer need the intermediate plug-in */
   /* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */
   (*plugInInterface)->Release (plugInInterface);
-  if (kresult || !cInterface->interface) {
+  if (kresult != kIOReturnSuccess || !cInterface->interface) {
     usbi_err (HANDLE_CTX (dev_handle), "QueryInterface: %s", darwin_error_str(kresult));
     return darwin_to_libusb (kresult);
   }
 
   /* claim the interface */
   kresult = (*(cInterface->interface))->USBInterfaceOpen(cInterface->interface);
-  if (kresult) {
+  if (kresult != kIOReturnSuccess) {
     usbi_err (HANDLE_CTX (dev_handle), "USBInterfaceOpen: %s", darwin_error_str(kresult));
     return darwin_to_libusb (kresult);
   }
 
   /* update list of endpoints */
-  kresult = get_endpoints (dev_handle, iface);
-  if (kresult) {
+  ret = get_endpoints (dev_handle, iface);
+  if (ret) {
     /* this should not happen */
     darwin_release_interface (dev_handle, iface);
     usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table");
-    return kresult;
+    return ret;
   }
 
   cInterface->cfSource = NULL;
@@ -1406,11 +1519,11 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int i
 
   usbi_dbg ("interface opened");
 
-  return 0;
+  return LIBUSB_SUCCESS;
 }
 
-static int darwin_release_interface(struct libusb_device_handle *dev_handle, int iface) {
-  struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+static int darwin_release_interface(struct libusb_device_handle *dev_handle, uint8_t iface) {
+  struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
   IOReturn kresult;
 
   /* current interface */
@@ -1430,7 +1543,7 @@ static int darwin_release_interface(struct libusb_device_handle *dev_handle, int
   }
 
   kresult = (*(cInterface->interface))->USBInterfaceClose(cInterface->interface);
-  if (kresult)
+  if (kresult != kIOReturnSuccess)
     usbi_warn (HANDLE_CTX (dev_handle), "USBInterfaceClose: %s", darwin_error_str(kresult));
 
   kresult = (*(cInterface->interface))->Release(cInterface->interface);
@@ -1442,9 +1555,10 @@ static int darwin_release_interface(struct libusb_device_handle *dev_handle, int
   return darwin_to_libusb (kresult);
 }
 
-static int darwin_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) {
-  struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
+static int darwin_set_interface_altsetting(struct libusb_device_handle *dev_handle, uint8_t iface, uint8_t altsetting) {
+  struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
   IOReturn kresult;
+  enum libusb_error ret;
 
   /* current interface */
   struct darwin_interface *cInterface = &priv->interfaces[iface];
@@ -1457,12 +1571,12 @@ static int darwin_set_interface_altsetting(struct libusb_device_handle *dev_hand
     darwin_reset_device (dev_handle);
 
   /* update list of endpoints */
-  kresult = get_endpoints (dev_handle, iface);
-  if (kresult) {
+  ret = get_endpoints (dev_handle, iface);
+  if (ret) {
     /* this should not happen */
     darwin_release_interface (dev_handle, iface);
     usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table");
-    return kresult;
+    return ret;
   }
 
   return darwin_to_libusb (kresult);
@@ -1483,72 +1597,143 @@ static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned c
 
   /* newer versions of darwin support clearing additional bits on the device's endpoint */
   kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef);
-  if (kresult)
+  if (kresult != kIOReturnSuccess)
     usbi_warn (HANDLE_CTX (dev_handle), "ClearPipeStall: %s", darwin_error_str (kresult));
 
   return darwin_to_libusb (kresult);
 }
 
+static int darwin_restore_state (struct libusb_device_handle *dev_handle, int8_t active_config,
+                                 unsigned long claimed_interfaces) {
+  struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+  struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
+  int open_count = dpriv->open_count;
+  int ret;
+
+  /* clear claimed interfaces temporarily */
+  dev_handle->claimed_interfaces = 0;
+
+  /* close and re-open the device */
+  priv->is_open = false;
+  dpriv->open_count = 1;
+
+  /* clean up open interfaces */
+  (void) darwin_close (dev_handle);
+
+  /* re-open the device */
+  ret = darwin_open (dev_handle);
+  dpriv->open_count = open_count;
+  if (LIBUSB_SUCCESS != ret) {
+    /* could not restore configuration */
+    return LIBUSB_ERROR_NOT_FOUND;
+  }
+
+  if (dpriv->active_config != active_config) {
+    usbi_dbg ("darwin/restore_state: restoring configuration %d...", active_config);
+
+    ret = darwin_set_configuration (dev_handle, active_config);
+    if (LIBUSB_SUCCESS != ret) {
+      usbi_dbg ("darwin/restore_state: could not restore configuration");
+      return LIBUSB_ERROR_NOT_FOUND;
+    }
+  }
+
+  usbi_dbg ("darwin/restore_state: reclaiming interfaces");
+
+  if (claimed_interfaces) {
+    for (uint8_t iface = 0 ; iface < USB_MAXINTERFACES ; ++iface) {
+      if (!(claimed_interfaces & (1U << iface))) {
+        continue;
+      }
+
+      usbi_dbg ("darwin/restore_state: re-claiming interface %u", iface);
+
+      ret = darwin_claim_interface (dev_handle, iface);
+      if (LIBUSB_SUCCESS != ret) {
+        usbi_dbg ("darwin/restore_state: could not claim interface %u", iface);
+        return LIBUSB_ERROR_NOT_FOUND;
+      }
+
+      dev_handle->claimed_interfaces |= 1U << iface;
+    }
+  }
+
+  usbi_dbg ("darwin/restore_state: device state restored");
+
+  return LIBUSB_SUCCESS;
+}
+
 static int darwin_reset_device(struct libusb_device_handle *dev_handle) {
   struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
+  unsigned long claimed_interfaces = dev_handle->claimed_interfaces;
+  int8_t active_config = dpriv->active_config;
   IOUSBDeviceDescriptor descriptor;
   IOUSBConfigurationDescriptorPtr cached_configuration;
-  IOUSBConfigurationDescriptor configuration;
-  bool reenumerate = false;
+  IOUSBConfigurationDescriptor *cached_configurations;
   IOReturn kresult;
-  int i;
+  UInt8 i;
 
-  kresult = (*(dpriv->device))->ResetDevice (dpriv->device);
-  if (kresult) {
-    usbi_err (HANDLE_CTX (dev_handle), "ResetDevice: %s", darwin_error_str (kresult));
-    return darwin_to_libusb (kresult);
+  if (dpriv->in_reenumerate) {
+    /* ack, two (or more) threads are trying to reset the device! abort! */
+    return LIBUSB_ERROR_NOT_FOUND;
   }
 
-  do {
-    usbi_dbg ("darwin/reset_device: checking if device descriptor changed");
+  dpriv->in_reenumerate = true;
 
-    /* ignore return code. if we can't get a descriptor it might be worthwhile re-enumerating anway */
-    (void) darwin_request_descriptor (dpriv->device, kUSBDeviceDesc, 0, &descriptor, sizeof (descriptor));
+  /* store copies of descriptors so they can be compared after the reset */
+  memcpy (&descriptor, &dpriv->dev_descriptor, sizeof (descriptor));
+  cached_configurations = alloca (sizeof (*cached_configurations) * descriptor.bNumConfigurations);
 
-    /* check if the device descriptor has changed */
-    if (0 != memcmp (&dpriv->dev_descriptor, &descriptor, sizeof (descriptor))) {
-      reenumerate = true;
-      break;
-    }
+  for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) {
+    (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration);
+    memcpy (cached_configurations + i, cached_configuration, sizeof (cached_configurations[i]));
+  }
 
-    /* check if any configuration descriptor has changed */
-    for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) {
-      usbi_dbg ("darwin/reset_device: checking if configuration descriptor %d changed", i);
+  /* from macOS 10.11 ResetDevice no longer does anything so just use USBDeviceReEnumerate */
+  kresult = (*(dpriv->device))->USBDeviceReEnumerate (dpriv->device, 0);
+  if (kresult != kIOReturnSuccess) {
+    usbi_err (HANDLE_CTX (dev_handle), "USBDeviceReEnumerate: %s", darwin_error_str (kresult));
+    dpriv->in_reenumerate = false;
+    return darwin_to_libusb (kresult);
+  }
 
-      (void) darwin_request_descriptor (dpriv->device, kUSBConfDesc, i, &configuration, sizeof (configuration));
-      (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration);
+  usbi_dbg ("darwin/reset_device: waiting for re-enumeration to complete...");
 
-      if (!cached_configuration || 0 != memcmp (cached_configuration, &configuration, sizeof (configuration))) {
-        reenumerate = true;
-        break;
-      }
-    }
-  } while (0);
+  while (dpriv->in_reenumerate) {
+    struct timespec delay = {.tv_sec = 0, .tv_nsec = 1000};
+    nanosleep (&delay, NULL);
+  }
+
+  /* compare descriptors */
+  usbi_dbg ("darwin/reset_device: checking whether descriptors changed");
 
-  if (reenumerate) {
-    usbi_dbg ("darwin/reset_device: device requires reenumeration");
-    (void) (*(dpriv->device))->USBDeviceReEnumerate (dpriv->device, 0);
+  if (memcmp (&descriptor, &dpriv->dev_descriptor, sizeof (descriptor))) {
+    /* device descriptor changed. need to return not found. */
+    usbi_dbg ("darwin/reset_device: device descriptor changed");
     return LIBUSB_ERROR_NOT_FOUND;
   }
 
-  usbi_dbg ("darwin/reset_device: device reset complete");
+  for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) {
+    (void) (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration);
+    if (memcmp (cached_configuration, cached_configurations + i, sizeof (cached_configurations[i]))) {
+      usbi_dbg ("darwin/reset_device: configuration descriptor %d changed", i);
+      return LIBUSB_ERROR_NOT_FOUND;
+    }
+  }
 
-  return LIBUSB_SUCCESS;
+  usbi_dbg ("darwin/reset_device: device reset complete. restoring state...");
+
+  return darwin_restore_state (dev_handle, active_config, claimed_interfaces);
 }
 
-static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle, int interface) {
+static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle, uint8_t interface) {
   struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
   io_service_t usbInterface;
   CFTypeRef driver;
   IOReturn kresult;
 
   kresult = darwin_get_interface (dpriv->device, interface, &usbInterface);
-  if (kresult) {
+  if (kresult != kIOReturnSuccess) {
     usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult));
 
     return darwin_to_libusb (kresult);
@@ -1567,21 +1752,8 @@ static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle,
   return 0;
 }
 
-/* attaching/detaching kernel drivers is not currently supported (maybe in the future?) */
-static int darwin_attach_kernel_driver (struct libusb_device_handle *dev_handle, int interface) {
-  UNUSED(dev_handle);
-  UNUSED(interface);
-  return LIBUSB_ERROR_NOT_SUPPORTED;
-}
-
-static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, int interface) {
-  UNUSED(dev_handle);
-  UNUSED(interface);
-  return LIBUSB_ERROR_NOT_SUPPORTED;
-}
-
 static void darwin_destroy_device(struct libusb_device *dev) {
-  struct darwin_device_priv *dpriv = (struct darwin_device_priv *) dev->os_priv;
+  struct darwin_device_priv *dpriv = usbi_get_device_priv(dev);
 
   if (dpriv->dev) {
     /* need to hold the lock in case this is the last reference to the device */
@@ -1597,11 +1769,16 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) {
 
   IOReturn               ret;
   uint8_t                transferType;
-  /* None of the values below are used in libusbx for bulk transfers */
-  uint8_t                direction, number, interval, pipeRef;
+  uint8_t                pipeRef;
   uint16_t               maxPacketSize;
 
   struct darwin_interface *cInterface;
+#if InterfaceVersion >= 550
+  IOUSBEndpointProperties pipeProperties = {.bVersion = kUSBEndpointPropertiesVersion3};
+#else
+  /* None of the values below are used in libusb for bulk transfers */
+  uint8_t                 direction, number, interval;
+#endif
 
   if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) {
     usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
@@ -1609,8 +1786,15 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) {
     return LIBUSB_ERROR_NOT_FOUND;
   }
 
+#if InterfaceVersion >= 550
+  ret = (*(cInterface->interface))->GetPipePropertiesV3 (cInterface->interface, pipeRef, &pipeProperties);
+
+  transferType = pipeProperties.bTransferType;
+  maxPacketSize = pipeProperties.wMaxPacketSize;
+#else
   ret = (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
                                                        &transferType, &maxPacketSize, &interval);
+#endif
 
   if (ret) {
     usbi_err (TRANSFER_CTX (transfer), "bulk transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out",
@@ -1628,21 +1812,21 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) {
   if (transferType == kUSBInterrupt) {
     if (IS_XFERIN(transfer))
       ret = (*(cInterface->interface))->ReadPipeAsync(cInterface->interface, pipeRef, transfer->buffer,
-                                                      transfer->length, darwin_async_io_callback, itransfer);
+                                                      (UInt32)transfer->length, darwin_async_io_callback, itransfer);
     else
       ret = (*(cInterface->interface))->WritePipeAsync(cInterface->interface, pipeRef, transfer->buffer,
-                                                       transfer->length, darwin_async_io_callback, itransfer);
+                                                       (UInt32)transfer->length, darwin_async_io_callback, itransfer);
   } else {
     itransfer->timeout_flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT;
 
     if (IS_XFERIN(transfer))
       ret = (*(cInterface->interface))->ReadPipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer,
-                                                        transfer->length, transfer->timeout, transfer->timeout,
-                                                        darwin_async_io_callback, (void *)itransfer);
+                                                        (UInt32)transfer->length, transfer->timeout, transfer->timeout,
+                                                        darwin_async_io_callback, itransfer);
     else
       ret = (*(cInterface->interface))->WritePipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer,
-                                                         transfer->length, transfer->timeout, transfer->timeout,
-                                                         darwin_async_io_callback, (void *)itransfer);
+                                                         (UInt32)transfer->length, transfer->timeout, transfer->timeout,
+                                                         darwin_async_io_callback, itransfer);
   }
 
   if (ret)
@@ -1669,12 +1853,12 @@ static int submit_stream_transfer(struct usbi_transfer *itransfer) {
 
   if (IS_XFERIN(transfer))
     ret = (*(cInterface->interface))->ReadStreamsPipeAsyncTO(cInterface->interface, pipeRef, itransfer->stream_id,
-                                                             transfer->buffer, transfer->length, transfer->timeout,
-                                                             transfer->timeout, darwin_async_io_callback, (void *)itransfer);
+                                                             transfer->buffer, (UInt32)transfer->length, transfer->timeout,
+                                                             transfer->timeout, darwin_async_io_callback, itransfer);
   else
     ret = (*(cInterface->interface))->WriteStreamsPipeAsyncTO(cInterface->interface, pipeRef, itransfer->stream_id,
-                                                              transfer->buffer, transfer->length, transfer->timeout,
-                                                              transfer->timeout, darwin_async_io_callback, (void *)itransfer);
+                                                              transfer->buffer, (UInt32)transfer->length, transfer->timeout,
+                                                              transfer->timeout, darwin_async_io_callback, itransfer);
 
   if (ret)
     usbi_err (TRANSFER_CTX (transfer), "bulk stream transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out",
@@ -1686,7 +1870,7 @@ static int submit_stream_transfer(struct usbi_transfer *itransfer) {
 
 static int submit_iso_transfer(struct usbi_transfer *itransfer) {
   struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
-  struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+  struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer);
 
   IOReturn kresult;
   uint8_t direction, number, interval, pipeRef, transferType;
@@ -1697,22 +1881,25 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) {
 
   struct darwin_interface *cInterface;
 
-  /* construct an array of IOUSBIsocFrames, reuse the old one if possible */
-  if (tpriv->isoc_framelist && tpriv->num_iso_packets != transfer->num_iso_packets) {
+  /* construct an array of IOUSBIsocFrames, reuse the old one if the sizes are the same */
+  if (tpriv->num_iso_packets != transfer->num_iso_packets) {
     free(tpriv->isoc_framelist);
     tpriv->isoc_framelist = NULL;
   }
 
   if (!tpriv->isoc_framelist) {
     tpriv->num_iso_packets = transfer->num_iso_packets;
-    tpriv->isoc_framelist = (IOUSBIsocFrame*) calloc (transfer->num_iso_packets, sizeof(IOUSBIsocFrame));
+    tpriv->isoc_framelist = (IOUSBIsocFrame*) calloc ((size_t)transfer->num_iso_packets, sizeof(IOUSBIsocFrame));
     if (!tpriv->isoc_framelist)
       return LIBUSB_ERROR_NO_MEM;
   }
 
   /* copy the frame list from the libusb descriptor (the structures differ only is member order) */
-  for (i = 0 ; i < transfer->num_iso_packets ; i++)
-    tpriv->isoc_framelist[i].frReqCount = transfer->iso_packet_desc[i].length;
+  for (i = 0 ; i < transfer->num_iso_packets ; i++) {
+    unsigned int length = transfer->iso_packet_desc[i].length;
+    assert(length <= UINT16_MAX);
+    tpriv->isoc_framelist[i].frReqCount = (UInt16)length;
+  }
 
   /* determine the interface/endpoint to use */
   if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) {
@@ -1727,7 +1914,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) {
 
   /* Last but not least we need the bus frame number */
   kresult = (*(cInterface->interface))->GetBusFrameNumber(cInterface->interface, &frame, &atTime);
-  if (kresult) {
+  if (kresult != kIOReturnSuccess) {
     usbi_err (TRANSFER_CTX (transfer), "failed to get bus frame number: %d", kresult);
     free(tpriv->isoc_framelist);
     tpriv->isoc_framelist = NULL;
@@ -1747,19 +1934,19 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) {
   /* submit the request */
   if (IS_XFERIN(transfer))
     kresult = (*(cInterface->interface))->ReadIsochPipeAsync(cInterface->interface, pipeRef, transfer->buffer, frame,
-                                                             transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback,
+                                                             (UInt32)transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback,
                                                              itransfer);
   else
     kresult = (*(cInterface->interface))->WriteIsochPipeAsync(cInterface->interface, pipeRef, transfer->buffer, frame,
-                                                              transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback,
+                                                              (UInt32)transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback,
                                                               itransfer);
 
   if (LIBUSB_SPEED_FULL == transfer->dev_handle->dev->speed)
     /* Full speed */
-    cInterface->frames[transfer->endpoint] = frame + transfer->num_iso_packets * (1 << (interval - 1));
+    cInterface->frames[transfer->endpoint] = frame + (UInt32)transfer->num_iso_packets * (1U << (interval - 1));
   else
     /* High/super speed */
-    cInterface->frames[transfer->endpoint] = frame + transfer->num_iso_packets * (1 << (interval - 1)) / 8;
+    cInterface->frames[transfer->endpoint] = frame + (UInt32)transfer->num_iso_packets * (1U << (interval - 1)) / 8;
 
   if (kresult != kIOReturnSuccess) {
     usbi_err (TRANSFER_CTX (transfer), "isochronous transfer failed (dir: %s): %s", IS_XFERIN(transfer) ? "In" : "Out",
@@ -1775,7 +1962,7 @@ static int submit_control_transfer(struct usbi_transfer *itransfer) {
   struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
   struct libusb_control_setup *setup = (struct libusb_control_setup *) transfer->buffer;
   struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev);
-  struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+  struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer);
 
   IOReturn               kresult;
 
@@ -1907,20 +2094,10 @@ static int darwin_cancel_transfer(struct usbi_transfer *itransfer) {
   }
 }
 
-static void darwin_clear_transfer_priv (struct usbi_transfer *itransfer) {
-  struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
-  struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
-
-  if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS && tpriv->isoc_framelist) {
-    free (tpriv->isoc_framelist);
-    tpriv->isoc_framelist = NULL;
-  }
-}
-
 static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0) {
   struct usbi_transfer *itransfer = (struct usbi_transfer *)refcon;
   struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
-  struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
+  struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer);
 
   usbi_dbg ("an async io operation has completed");
 
@@ -1941,7 +2118,7 @@ static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0)
   usbi_signal_transfer_completion(itransfer);
 }
 
-static int darwin_transfer_status (struct usbi_transfer *itransfer, kern_return_t result) {
+static enum libusb_transfer_status darwin_transfer_status (struct usbi_transfer *itransfer, IOReturn result) {
   if (itransfer->timeout_flags & USBI_TRANSFER_TIMED_OUT)
     result = kIOUSBTransactionTimeout;
 
@@ -1969,73 +2146,62 @@ static int darwin_transfer_status (struct usbi_transfer *itransfer, kern_return_
 
 static int darwin_handle_transfer_completion (struct usbi_transfer *itransfer) {
   struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
-  struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
-  int isIsoc      = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS == transfer->type;
-  int isBulk      = LIBUSB_TRANSFER_TYPE_BULK == transfer->type;
-  int isControl   = LIBUSB_TRANSFER_TYPE_CONTROL == transfer->type;
-  int isInterrupt = LIBUSB_TRANSFER_TYPE_INTERRUPT == transfer->type;
-  int i;
+  struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer);
+  const unsigned char max_transfer_type = LIBUSB_TRANSFER_TYPE_BULK_STREAM;
+  const char *transfer_types[max_transfer_type + 1] = {"control", "isoc", "bulk", "interrupt", "bulk-stream"};
+  bool is_isoc = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS == transfer->type;
 
-  if (!isIsoc && !isBulk && !isControl && !isInterrupt) {
+  if (transfer->type > max_transfer_type) {
     usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
     return LIBUSB_ERROR_INVALID_PARAM;
   }
 
-  usbi_dbg ("handling %s completion with kernel status %d",
-             isControl ? "control" : isBulk ? "bulk" : isIsoc ? "isoc" : "interrupt", tpriv->result);
+  if (NULL == tpriv) {
+    usbi_err (TRANSFER_CTX(transfer), "malformed request is missing transfer priv");
+    return LIBUSB_ERROR_INVALID_PARAM;
+  }
+
+  usbi_dbg ("handling transfer completion type %s with kernel status %d", transfer_types[transfer->type], tpriv->result);
 
   if (kIOReturnSuccess == tpriv->result || kIOReturnUnderrun == tpriv->result) {
-    if (isIsoc && tpriv->isoc_framelist) {
+    if (is_isoc && tpriv->isoc_framelist) {
       /* copy isochronous results back */
 
-      for (i = 0; i < transfer->num_iso_packets ; i++) {
+      for (int i = 0; i < transfer->num_iso_packets ; i++) {
         struct libusb_iso_packet_descriptor *lib_desc = &transfer->iso_packet_desc[i];
-        lib_desc->status = darwin_to_libusb (tpriv->isoc_framelist[i].frStatus);
+        lib_desc->status = darwin_transfer_status (itransfer, tpriv->isoc_framelist[i].frStatus);
         lib_desc->actual_length = tpriv->isoc_framelist[i].frActCount;
       }
-    } else if (!isIsoc)
+    } else if (!is_isoc) {
       itransfer->transferred += tpriv->size;
+    }
   }
 
   /* it is ok to handle cancelled transfers without calling usbi_handle_transfer_cancellation (we catch timeout transfers) */
   return usbi_handle_transfer_completion (itransfer, darwin_transfer_status (itransfer, tpriv->result));
 }
 
-static int darwin_clock_gettime(int clk_id, struct timespec *tp) {
-#if !OSX_USE_CLOCK_GETTIME
+#if !defined(HAVE_CLOCK_GETTIME)
+void usbi_get_monotonic_time(struct timespec *tp) {
   mach_timespec_t sys_time;
-  clock_serv_t clock_ref;
-
-  switch (clk_id) {
-  case USBI_CLOCK_REALTIME:
-    /* CLOCK_REALTIME represents time since the epoch */
-    clock_ref = clock_realtime;
-    break;
-  case USBI_CLOCK_MONOTONIC:
-    /* use system boot time as reference for the monotonic clock */
-    clock_ref = clock_monotonic;
-    break;
-  default:
-    return LIBUSB_ERROR_INVALID_PARAM;
-  }
 
-  clock_get_time (clock_ref, &sys_time);
+  /* use system boot time as reference for the monotonic clock */
+  clock_get_time (clock_monotonic, &sys_time);
 
   tp->tv_sec  = sys_time.tv_sec;
   tp->tv_nsec = sys_time.tv_nsec;
+}
 
-  return 0;
-#else
-  switch (clk_id) {
-  case USBI_CLOCK_MONOTONIC:
-    return clock_gettime(CLOCK_MONOTONIC, tp);
-  case USBI_CLOCK_REALTIME:
-    return clock_gettime(CLOCK_REALTIME, tp);
-  default:
-    return LIBUSB_ERROR_INVALID_PARAM;
-  }
-#endif
+void usbi_get_real_time(struct timespec *tp) {
+  mach_timespec_t sys_time;
+
+  /* CLOCK_REALTIME represents time since the epoch */
+  clock_get_time (clock_realtime, &sys_time);
+
+  tp->tv_sec  = sys_time.tv_sec;
+  tp->tv_nsec = sys_time.tv_nsec;
 }
+#endif
 
 #if InterfaceVersion >= 550
 static int darwin_alloc_streams (struct libusb_device_handle *dev_handle, uint32_t num_streams, unsigned char *endpoints,
@@ -2045,7 +2211,7 @@ static int darwin_alloc_streams (struct libusb_device_handle *dev_handle, uint32
   uint8_t pipeRef;
   int rc, i;
 
-  /* find the mimimum number of supported streams on the endpoint list */
+  /* find the minimum number of supported streams on the endpoint list */
   for (i = 0 ; i < num_endpoints ; ++i) {
     if (0 != (rc = ep_to_pipeRef (dev_handle, endpoints[i], &pipeRef, NULL, &cInterface))) {
       return rc;
@@ -2069,7 +2235,8 @@ static int darwin_alloc_streams (struct libusb_device_handle *dev_handle, uint32
       return darwin_to_libusb(rc);
   }
 
-  return num_streams;
+  assert(num_streams <= INT_MAX);
+  return (int)num_streams;
 }
 
 static int darwin_free_streams (struct libusb_device_handle *dev_handle, unsigned char *endpoints, int num_endpoints) {
@@ -2100,8 +2267,6 @@ const struct usbi_os_backend usbi_backend = {
         .caps = 0,
         .init = darwin_init,
         .exit = darwin_exit,
-        .get_device_list = NULL, /* not needed */
-        .get_device_descriptor = darwin_get_device_descriptor,
         .get_active_config_descriptor = darwin_get_active_config_descriptor,
         .get_config_descriptor = darwin_get_config_descriptor,
         .hotplug_poll = darwin_hotplug_poll,
@@ -2123,19 +2288,14 @@ const struct usbi_os_backend usbi_backend = {
 #endif
 
         .kernel_driver_active = darwin_kernel_driver_active,
-        .detach_kernel_driver = darwin_detach_kernel_driver,
-        .attach_kernel_driver = darwin_attach_kernel_driver,
 
         .destroy_device = darwin_destroy_device,
 
         .submit_transfer = darwin_submit_transfer,
         .cancel_transfer = darwin_cancel_transfer,
-        .clear_transfer_priv = darwin_clear_transfer_priv,
 
         .handle_transfer_completion = darwin_handle_transfer_completion,
 
-        .clock_gettime = darwin_clock_gettime,
-
         .device_priv_size = sizeof(struct darwin_device_priv),
         .device_handle_priv_size = sizeof(struct darwin_device_handle_priv),
         .transfer_priv_size = sizeof(struct darwin_transfer_priv),
index 474567f6ac774e80c6931c55353aa2edda2830d8..b799bfd4429e593fadb95b3e0704ea197a9b3177 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * darwin backend for libusb 1.0
- * Copyright © 2008-2015 Nathan Hjelm <hjelmn@users.sourceforge.net>
+ * Copyright © 2008-2019 Nathan Hjelm <hjelmn@users.sourceforge.net>
+ * Copyright © 2019      Google LLC. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -20,6 +21,8 @@
 #if !defined(LIBUSB_DARWIN_H)
 #define LIBUSB_DARWIN_H
 
+#include <stdbool.h>
+
 #include "libusbi.h"
 
 #include <IOKit/IOTypes.h>
@@ -155,13 +158,14 @@ struct darwin_cached_device {
   UInt32                location;
   UInt64                parent_session;
   UInt64                session;
-  UInt16                address;
+  USBDeviceAddress      address;
   char                  sys_path[21];
   usb_device_t        **device;
   int                   open_count;
-  UInt8                 first_config, active_config, port;  
+  UInt8                 first_config, active_config, port;
   int                   can_enumerate;
   int                   refcount;
+  bool                  in_reenumerate;
 };
 
 struct darwin_device_priv {
@@ -169,7 +173,7 @@ struct darwin_device_priv {
 };
 
 struct darwin_device_handle_priv {
-  int                  is_open;
+  bool                 is_open;
   CFRunLoopSourceRef   cfSource;
 
   struct darwin_interface {
diff --git a/mac/libusb/os/events_posix.c b/mac/libusb/os/events_posix.c
new file mode 100644 (file)
index 0000000..b74189b
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * libusb event abstraction on POSIX platforms
+ *
+ * Copyright © 2020 Chris Dickens <christopher.a.dickens@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libusbi.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_EVENTFD
+#include <sys/eventfd.h>
+#endif
+#ifdef HAVE_TIMERFD
+#include <sys/timerfd.h>
+#endif
+#include <unistd.h>
+
+#ifdef HAVE_EVENTFD
+#define EVENT_READ_FD(e)       ((e)->eventfd)
+#define EVENT_WRITE_FD(e)      ((e)->eventfd)
+#else
+#define EVENT_READ_FD(e)       ((e)->pipefd[0])
+#define EVENT_WRITE_FD(e)      ((e)->pipefd[1])
+#endif
+
+#ifdef HAVE_NFDS_T
+typedef nfds_t usbi_nfds_t;
+#else
+typedef unsigned int usbi_nfds_t;
+#endif
+
+int usbi_create_event(usbi_event_t *event)
+{
+#ifdef HAVE_EVENTFD
+       event->eventfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
+       if (event->eventfd == -1) {
+               usbi_err(NULL, "failed to create eventfd, errno=%d", errno);
+               return LIBUSB_ERROR_OTHER;
+       }
+
+       return 0;
+#else
+#if defined(HAVE_PIPE2)
+       int ret = pipe2(event->pipefd, O_CLOEXEC);
+#else
+       int ret = pipe(event->pipefd);
+#endif
+
+       if (ret != 0) {
+               usbi_err(NULL, "failed to create pipe, errno=%d", errno);
+               return LIBUSB_ERROR_OTHER;
+       }
+
+#if !defined(HAVE_PIPE2) && defined(FD_CLOEXEC)
+       ret = fcntl(event->pipefd[0], F_GETFD);
+       if (ret == -1) {
+               usbi_err(NULL, "failed to get pipe fd flags, errno=%d", errno);
+               goto err_close_pipe;
+       }
+       ret = fcntl(event->pipefd[0], F_SETFD, ret | FD_CLOEXEC);
+       if (ret == -1) {
+               usbi_err(NULL, "failed to set pipe fd flags, errno=%d", errno);
+               goto err_close_pipe;
+       }
+
+       ret = fcntl(event->pipefd[1], F_GETFD);
+       if (ret == -1) {
+               usbi_err(NULL, "failed to get pipe fd flags, errno=%d", errno);
+               goto err_close_pipe;
+       }
+       ret = fcntl(event->pipefd[1], F_SETFD, ret | FD_CLOEXEC);
+       if (ret == -1) {
+               usbi_err(NULL, "failed to set pipe fd flags, errno=%d", errno);
+               goto err_close_pipe;
+       }
+#endif
+
+       ret = fcntl(event->pipefd[1], F_GETFL);
+       if (ret == -1) {
+               usbi_err(NULL, "failed to get pipe fd status flags, errno=%d", errno);
+               goto err_close_pipe;
+       }
+       ret = fcntl(event->pipefd[1], F_SETFL, ret | O_NONBLOCK);
+       if (ret == -1) {
+               usbi_err(NULL, "failed to set pipe fd status flags, errno=%d", errno);
+               goto err_close_pipe;
+       }
+
+       return 0;
+
+err_close_pipe:
+       close(event->pipefd[1]);
+       close(event->pipefd[0]);
+       return LIBUSB_ERROR_OTHER;
+#endif
+}
+
+void usbi_destroy_event(usbi_event_t *event)
+{
+#ifdef HAVE_EVENTFD
+       if (close(event->eventfd) == -1)
+               usbi_warn(NULL, "failed to close eventfd, errno=%d", errno);
+#else
+       if (close(event->pipefd[1]) == -1)
+               usbi_warn(NULL, "failed to close pipe write end, errno=%d", errno);
+       if (close(event->pipefd[0]) == -1)
+               usbi_warn(NULL, "failed to close pipe read end, errno=%d", errno);
+#endif
+}
+
+void usbi_signal_event(usbi_event_t *event)
+{
+       uint64_t dummy = 1;
+       ssize_t r;
+
+       r = write(EVENT_WRITE_FD(event), &dummy, sizeof(dummy));
+       if (r != sizeof(dummy))
+               usbi_warn(NULL, "event write failed");
+}
+
+void usbi_clear_event(usbi_event_t *event)
+{
+       uint64_t dummy;
+       ssize_t r;
+
+       r = read(EVENT_READ_FD(event), &dummy, sizeof(dummy));
+       if (r != sizeof(dummy))
+               usbi_warn(NULL, "event read failed");
+}
+
+#ifdef HAVE_TIMERFD
+int usbi_create_timer(usbi_timer_t *timer)
+{
+       timer->timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
+       if (timer->timerfd == -1) {
+               usbi_warn(NULL, "failed to create timerfd, errno=%d", errno);
+               return LIBUSB_ERROR_OTHER;
+       }
+
+       return 0;
+}
+
+void usbi_destroy_timer(usbi_timer_t *timer)
+{
+       if (close(timer->timerfd) == -1)
+               usbi_warn(NULL, "failed to close timerfd, errno=%d", errno);
+}
+
+int usbi_arm_timer(usbi_timer_t *timer, const struct timespec *timeout)
+{
+       const struct itimerspec it = { { 0, 0 }, { timeout->tv_sec, timeout->tv_nsec } };
+
+       if (timerfd_settime(timer->timerfd, TFD_TIMER_ABSTIME, &it, NULL) == -1) {
+               usbi_warn(NULL, "failed to arm timerfd, errno=%d", errno);
+               return LIBUSB_ERROR_OTHER;
+       }
+
+       return 0;
+}
+
+int usbi_disarm_timer(usbi_timer_t *timer)
+{
+       const struct itimerspec it = { { 0, 0 }, { 0, 0 } };
+
+       if (timerfd_settime(timer->timerfd, 0, &it, NULL) == -1) {
+               usbi_warn(NULL, "failed to disarm timerfd, errno=%d", errno);
+               return LIBUSB_ERROR_OTHER;
+       }
+
+       return 0;
+}
+#endif
+
+int usbi_alloc_event_data(struct libusb_context *ctx)
+{
+       struct usbi_event_source *ievent_source;
+       struct pollfd *fds;
+       size_t i = 0;
+
+       if (ctx->event_data) {
+               free(ctx->event_data);
+               ctx->event_data = NULL;
+       }
+
+       ctx->event_data_cnt = 0;
+       for_each_event_source(ctx, ievent_source)
+               ctx->event_data_cnt++;
+
+       fds = calloc(ctx->event_data_cnt, sizeof(*fds));
+       if (!fds)
+               return LIBUSB_ERROR_NO_MEM;
+
+       for_each_event_source(ctx, ievent_source) {
+               fds[i].fd = ievent_source->data.os_handle;
+               fds[i].events = ievent_source->data.poll_events;
+               i++;
+       }
+
+       ctx->event_data = fds;
+       return 0;
+}
+
+int usbi_wait_for_events(struct libusb_context *ctx,
+       struct usbi_reported_events *reported_events, int timeout_ms)
+{
+       struct pollfd *fds = ctx->event_data;
+       usbi_nfds_t nfds = (usbi_nfds_t)ctx->event_data_cnt;
+       int internal_fds, num_ready;
+
+       usbi_dbg("poll() %u fds with timeout in %dms", (unsigned int)nfds, timeout_ms);
+       num_ready = poll(fds, nfds, timeout_ms);
+       usbi_dbg("poll() returned %d", num_ready);
+       if (num_ready == 0) {
+               if (usbi_using_timer(ctx))
+                       goto done;
+               return LIBUSB_ERROR_TIMEOUT;
+       } else if (num_ready == -1) {
+               if (errno == EINTR)
+                       return LIBUSB_ERROR_INTERRUPTED;
+               usbi_err(ctx, "poll() failed, errno=%d", errno);
+               return LIBUSB_ERROR_IO;
+       }
+
+       /* fds[0] is always the internal signalling event */
+       if (fds[0].revents) {
+               reported_events->event_triggered = 1;
+               num_ready--;
+       } else {
+               reported_events->event_triggered = 0;
+       }
+
+#ifdef HAVE_OS_TIMER
+       /* on timer configurations, fds[1] is the timer */
+       if (usbi_using_timer(ctx) && fds[1].revents) {
+               reported_events->timer_triggered = 1;
+               num_ready--;
+       } else {
+               reported_events->timer_triggered = 0;
+       }
+#endif
+
+       if (!num_ready)
+               goto done;
+
+       /* the backend will never need to attempt to handle events on the
+        * library's internal file descriptors, so we determine how many are
+        * in use internally for this context and skip these when passing any
+        * remaining pollfds to the backend. */
+       internal_fds = usbi_using_timer(ctx) ? 2 : 1;
+       fds += internal_fds;
+       nfds -= internal_fds;
+
+       usbi_mutex_lock(&ctx->event_data_lock);
+       if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED) {
+               struct usbi_event_source *ievent_source;
+
+               for_each_removed_event_source(ctx, ievent_source) {
+                       usbi_nfds_t n;
+
+                       for (n = 0; n < nfds; n++) {
+                               if (ievent_source->data.os_handle != fds[n].fd)
+                                       continue;
+                               if (!fds[n].revents)
+                                       continue;
+                               /* pollfd was removed between the creation of the fds array and
+                                * here. remove triggered revent as it is no longer relevant. */
+                               usbi_dbg("fd %d was removed, ignoring raised events", fds[n].fd);
+                               fds[n].revents = 0;
+                               num_ready--;
+                               break;
+                       }
+               }
+       }
+       usbi_mutex_unlock(&ctx->event_data_lock);
+
+       if (num_ready) {
+               assert(num_ready > 0);
+               reported_events->event_data = fds;
+               reported_events->event_data_count = (unsigned int)nfds;
+       }
+
+done:
+       reported_events->num_ready = num_ready;
+       return LIBUSB_SUCCESS;
+}
diff --git a/mac/libusb/os/events_posix.h b/mac/libusb/os/events_posix.h
new file mode 100644 (file)
index 0000000..d81b5c4
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * libusb event abstraction on POSIX platforms
+ *
+ * Copyright © 2020 Chris Dickens <christopher.a.dickens@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef LIBUSB_EVENTS_POSIX_H
+#define LIBUSB_EVENTS_POSIX_H
+
+#include <poll.h>
+
+typedef int usbi_os_handle_t;
+#define USBI_OS_HANDLE_FORMAT_STRING   "fd %d"
+
+#ifdef HAVE_EVENTFD
+typedef struct usbi_event {
+       int eventfd;
+} usbi_event_t;
+#define USBI_EVENT_OS_HANDLE(e)        ((e)->eventfd)
+#define USBI_EVENT_POLL_EVENTS POLLIN
+#define USBI_INVALID_EVENT     { -1 }
+#else
+typedef struct usbi_event {
+       int pipefd[2];
+} usbi_event_t;
+#define USBI_EVENT_OS_HANDLE(e)        ((e)->pipefd[0])
+#define USBI_EVENT_POLL_EVENTS POLLIN
+#define USBI_INVALID_EVENT     { { -1, -1 } }
+#endif
+
+#ifdef HAVE_TIMERFD
+#define HAVE_OS_TIMER 1
+typedef struct usbi_timer {
+       int timerfd;
+} usbi_timer_t;
+#define USBI_TIMER_OS_HANDLE(t)        ((t)->timerfd)
+#define USBI_TIMER_POLL_EVENTS POLLIN
+
+static inline int usbi_timer_valid(usbi_timer_t *timer)
+{
+       return timer->timerfd >= 0;
+}
+#endif
+
+#endif
diff --git a/mac/libusb/os/poll_posix.c b/mac/libusb/os/poll_posix.c
deleted file mode 100644 (file)
index 337714a..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * poll_posix: poll compatibility wrapper for POSIX systems
- * Copyright © 2013 RealVNC Ltd.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#include <config.h>
-
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <stdlib.h>
-
-#include "libusbi.h"
-
-int usbi_pipe(int pipefd[2])
-{
-#if defined(HAVE_PIPE2)
-       int ret = pipe2(pipefd, O_CLOEXEC);
-#else
-       int ret = pipe(pipefd);
-#endif
-
-       if (ret != 0) {
-               usbi_err(NULL, "failed to create pipe (%d)", errno);
-               return ret;
-       }
-
-#if !defined(HAVE_PIPE2) && defined(FD_CLOEXEC)
-       ret = fcntl(pipefd[0], F_GETFD);
-       if (ret == -1) {
-               usbi_err(NULL, "failed to get pipe fd flags (%d)", errno);
-               goto err_close_pipe;
-       }
-       ret = fcntl(pipefd[0], F_SETFD, ret | FD_CLOEXEC);
-       if (ret == -1) {
-               usbi_err(NULL, "failed to set pipe fd flags (%d)", errno);
-               goto err_close_pipe;
-       }
-
-       ret = fcntl(pipefd[1], F_GETFD);
-       if (ret == -1) {
-               usbi_err(NULL, "failed to get pipe fd flags (%d)", errno);
-               goto err_close_pipe;
-       }
-       ret = fcntl(pipefd[1], F_SETFD, ret | FD_CLOEXEC);
-       if (ret == -1) {
-               usbi_err(NULL, "failed to set pipe fd flags (%d)", errno);
-               goto err_close_pipe;
-       }
-#endif
-
-       ret = fcntl(pipefd[1], F_GETFL);
-       if (ret == -1) {
-               usbi_err(NULL, "failed to get pipe fd status flags (%d)", errno);
-               goto err_close_pipe;
-       }
-       ret = fcntl(pipefd[1], F_SETFL, ret | O_NONBLOCK);
-       if (ret == -1) {
-               usbi_err(NULL, "failed to set pipe fd status flags (%d)", errno);
-               goto err_close_pipe;
-       }
-
-       return 0;
-
-err_close_pipe:
-       close(pipefd[0]);
-       close(pipefd[1]);
-       return ret;
-}
diff --git a/mac/libusb/os/poll_posix.h b/mac/libusb/os/poll_posix.h
deleted file mode 100644 (file)
index 5b4b2c9..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef LIBUSB_POLL_POSIX_H
-#define LIBUSB_POLL_POSIX_H
-
-#define usbi_write write
-#define usbi_read read
-#define usbi_close close
-#define usbi_poll poll
-
-int usbi_pipe(int pipefd[2]);
-
-#endif /* LIBUSB_POLL_POSIX_H */
index 16a7578b81ff001203475de79f603a42cd2ae2be..0e0e22134ebfcf6844cdb3006ab0545b35ef7b5f 100644 (file)
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#include <config.h>
+#include "libusbi.h"
 
-#include <time.h>
-#if defined(__linux__) || defined(__OpenBSD__)
-# if defined(__OpenBSD__)
-#  define _BSD_SOURCE
-# endif
+#include <errno.h>
+#if defined(__ANDROID__)
 # include <unistd.h>
+#elif defined(__HAIKU__)
+# include <os/kernel/OS.h>
+#elif defined(__linux__)
 # include <sys/syscall.h>
-#elif defined(__APPLE__)
-# include <pthread.h>
-#elif defined(__CYGWIN__)
-# include <windows.h>
+# include <unistd.h>
+#elif defined(__NetBSD__)
+# include <lwp.h>
+#elif defined(__OpenBSD__)
+# define _BSD_SOURCE
+# include <sys/syscall.h>
+# include <unistd.h>
+#elif defined(__sun__)
+# include <sys/lwp.h>
 #endif
 
-#include "threads_posix.h"
-#include "libusbi.h"
+void usbi_cond_init(pthread_cond_t *cond)
+{
+#ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK
+       pthread_condattr_t condattr;
+
+       PTHREAD_CHECK(pthread_condattr_init(&condattr));
+       PTHREAD_CHECK(pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC));
+       PTHREAD_CHECK(pthread_cond_init(cond, &condattr));
+       PTHREAD_CHECK(pthread_condattr_destroy(&condattr));
+#else
+       PTHREAD_CHECK(pthread_cond_init(cond, NULL));
+#endif
+}
 
 int usbi_cond_timedwait(pthread_cond_t *cond,
        pthread_mutex_t *mutex, const struct timeval *tv)
@@ -43,38 +59,71 @@ int usbi_cond_timedwait(pthread_cond_t *cond,
        struct timespec timeout;
        int r;
 
-       r = usbi_backend.clock_gettime(USBI_CLOCK_REALTIME, &timeout);
-       if (r < 0)
-               return r;
+#ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK
+       usbi_get_monotonic_time(&timeout);
+#else
+       usbi_get_real_time(&timeout);
+#endif
 
        timeout.tv_sec += tv->tv_sec;
-       timeout.tv_nsec += tv->tv_usec * 1000;
-       while (timeout.tv_nsec >= 1000000000L) {
-               timeout.tv_nsec -= 1000000000L;
+       timeout.tv_nsec += tv->tv_usec * 1000L;
+       if (timeout.tv_nsec >= NSEC_PER_SEC) {
+               timeout.tv_nsec -= NSEC_PER_SEC;
                timeout.tv_sec++;
        }
 
-       return pthread_cond_timedwait(cond, mutex, &timeout);
+       r = pthread_cond_timedwait(cond, mutex, &timeout);
+       if (r == 0)
+               return 0;
+       else if (r == ETIMEDOUT)
+               return LIBUSB_ERROR_TIMEOUT;
+       else
+               return LIBUSB_ERROR_OTHER;
 }
 
-int usbi_get_tid(void)
+unsigned int usbi_get_tid(void)
 {
-       int ret;
+       static _Thread_local unsigned int tl_tid;
+       int tid;
+
+       if (tl_tid)
+               return tl_tid;
+
 #if defined(__ANDROID__)
-       ret = gettid();
+       tid = gettid();
+#elif defined(__APPLE__)
+#ifdef HAVE_PTHREAD_THREADID_NP
+       uint64_t thread_id;
+
+       if (pthread_threadid_np(NULL, &thread_id) == 0)
+               tid = (int)thread_id;
+       else
+               tid = -1;
+#else
+       tid = (int)pthread_mach_thread_np(pthread_self());
+#endif
+#elif defined(__HAIKU__)
+       tid = get_pthread_thread_id(pthread_self());
 #elif defined(__linux__)
-       ret = syscall(SYS_gettid);
+       tid = (int)syscall(SYS_gettid);
+#elif defined(__NetBSD__)
+       tid = _lwp_self();
 #elif defined(__OpenBSD__)
        /* The following only works with OpenBSD > 5.1 as it requires
-          real thread support. For 5.1 and earlier, -1 is returned. */
-       ret = syscall(SYS_getthrid);
-#elif defined(__APPLE__)
-       ret = (int)pthread_mach_thread_np(pthread_self());
-#elif defined(__CYGWIN__)
-       ret = GetCurrentThreadId();
+        * real thread support. For 5.1 and earlier, -1 is returned. */
+       tid = syscall(SYS_getthrid);
+#elif defined(__sun__)
+       tid = _lwp_self();
 #else
-       ret = -1;
+       tid = -1;
 #endif
-/* TODO: NetBSD thread ID support */
-       return ret;
+
+       if (tid == -1) {
+               /* If we don't have a thread ID, at least return a unique
+                * value that can be used to distinguish individual
+                * threads. */
+               tid = (int)(intptr_t)pthread_self();
+       }
+
+       return tl_tid = (unsigned int)tid;
 }
index 9f1ef94bc7a87aa2266b09faa3034181631d4de3..932283402e36b4d6d36d231808aaf76d87c19ed0 100644 (file)
 #define LIBUSB_THREADS_POSIX_H
 
 #include <pthread.h>
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
+
+#define PTHREAD_CHECK(expression)      ASSERT_EQ(expression, 0)
 
 #define USBI_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
 typedef pthread_mutex_t usbi_mutex_static_t;
 static inline void usbi_mutex_static_lock(usbi_mutex_static_t *mutex)
 {
-       (void)pthread_mutex_lock(mutex);
+       PTHREAD_CHECK(pthread_mutex_lock(mutex));
 }
 static inline void usbi_mutex_static_unlock(usbi_mutex_static_t *mutex)
 {
-       (void)pthread_mutex_unlock(mutex);
+       PTHREAD_CHECK(pthread_mutex_unlock(mutex));
 }
 
 typedef pthread_mutex_t usbi_mutex_t;
-static inline int usbi_mutex_init(usbi_mutex_t *mutex)
+static inline void usbi_mutex_init(usbi_mutex_t *mutex)
 {
-       return pthread_mutex_init(mutex, NULL);
+       PTHREAD_CHECK(pthread_mutex_init(mutex, NULL));
 }
 static inline void usbi_mutex_lock(usbi_mutex_t *mutex)
 {
-       (void)pthread_mutex_lock(mutex);
+       PTHREAD_CHECK(pthread_mutex_lock(mutex));
 }
 static inline void usbi_mutex_unlock(usbi_mutex_t *mutex)
 {
-       (void)pthread_mutex_unlock(mutex);
+       PTHREAD_CHECK(pthread_mutex_unlock(mutex));
 }
 static inline int usbi_mutex_trylock(usbi_mutex_t *mutex)
 {
-       return pthread_mutex_trylock(mutex);
+       return pthread_mutex_trylock(mutex) == 0;
 }
 static inline void usbi_mutex_destroy(usbi_mutex_t *mutex)
 {
-       (void)pthread_mutex_destroy(mutex);
+       PTHREAD_CHECK(pthread_mutex_destroy(mutex));
 }
 
 typedef pthread_cond_t usbi_cond_t;
-static inline void usbi_cond_init(pthread_cond_t *cond)
-{
-       (void)pthread_cond_init(cond, NULL);
-}
-static inline int usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex)
+void usbi_cond_init(pthread_cond_t *cond);
+static inline void usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex)
 {
-       return pthread_cond_wait(cond, mutex);
+       PTHREAD_CHECK(pthread_cond_wait(cond, mutex));
 }
 int usbi_cond_timedwait(usbi_cond_t *cond,
        usbi_mutex_t *mutex, const struct timeval *tv);
 static inline void usbi_cond_broadcast(usbi_cond_t *cond)
 {
-       (void)pthread_cond_broadcast(cond);
+       PTHREAD_CHECK(pthread_cond_broadcast(cond));
 }
 static inline void usbi_cond_destroy(usbi_cond_t *cond)
 {
-       (void)pthread_cond_destroy(cond);
+       PTHREAD_CHECK(pthread_cond_destroy(cond));
 }
 
 typedef pthread_key_t usbi_tls_key_t;
 static inline void usbi_tls_key_create(usbi_tls_key_t *key)
 {
-       (void)pthread_key_create(key, NULL);
+       PTHREAD_CHECK(pthread_key_create(key, NULL));
 }
 static inline void *usbi_tls_key_get(usbi_tls_key_t key)
 {
@@ -90,13 +86,13 @@ static inline void *usbi_tls_key_get(usbi_tls_key_t key)
 }
 static inline void usbi_tls_key_set(usbi_tls_key_t key, void *ptr)
 {
-       (void)pthread_setspecific(key, ptr);
+       PTHREAD_CHECK(pthread_setspecific(key, ptr));
 }
 static inline void usbi_tls_key_delete(usbi_tls_key_t key)
 {
-       (void)pthread_key_delete(key);
+       PTHREAD_CHECK(pthread_key_delete(key));
 }
 
-int usbi_get_tid(void);
+unsigned int usbi_get_tid(void);
 
 #endif /* LIBUSB_THREADS_POSIX_H */
index d2be0e2a0088af6b71b0663621ee1d80292442c3..9445fa9e87ec2cf50084626cda7c0f2fe59bae5a 100644 (file)
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#include <config.h>
-
-#include <locale.h>
-#include <stdlib.h>
-#include <string.h>
-#if defined(HAVE_STRINGS_H)
-#include <strings.h>
-#endif
-
 #include "libusbi.h"
 
-#if defined(_MSC_VER)
-#define strncasecmp _strnicmp
-#endif
-
-static size_t usbi_locale = 0;
+#include <ctype.h>
+#include <string.h>
 
 /** \ingroup libusb_misc
  * How to add a new \ref libusb_strerror() translation:
  * <ol>
  * <li> Download the latest \c strerror.c from:<br>
- *      https://raw.github.com/libusb/libusb/master/libusb/sterror.c </li>
+ *      https://raw.github.com/libusb/libusb/master/libusb/strerror.c </li>
  * <li> Open the file in an UTF-8 capable editor </li>
  * <li> Add the 2 letter <a href="http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes">ISO 639-1</a>
  *      code for your locale at the end of \c usbi_locale_supported[]<br>
@@ -53,15 +41,15 @@ static size_t usbi_locale = 0;
  *         "Success",
  *         ...
  *         "Other error",
- *     }
+ *     },
  * };\endcode </li>
  * <li> Translate each of the English messages from the section you copied into your language </li>
  * <li> Save the file (in UTF-8 format) and send it to \c libusb-devel\@lists.sourceforge.net </li>
  * </ol>
  */
 
-static const char* usbi_locale_supported[] = { "en", "nl", "fr", "ru" };
-static const char* usbi_localized_errors[ARRAYSIZE(usbi_locale_supported)][LIBUSB_ERROR_COUNT] = {
+static const char * const usbi_locale_supported[] = { "en", "nl", "fr", "ru", "de", "hu" };
+static const char * const usbi_localized_errors[ARRAYSIZE(usbi_locale_supported)][LIBUSB_ERROR_COUNT] = {
        { /* English (en) */
                "Success",
                "Input/Output Error",
@@ -122,9 +110,41 @@ static const char* usbi_localized_errors[ARRAYSIZE(usbi_locale_supported)][LIBUS
                "Память исчерпана",
                "Операция не поддерживается данной платформой",
                "Неизвестная ошибка"
-       }
+       }, { /* German (de) */
+               "Erfolgreich",
+               "Eingabe-/Ausgabefehler",
+               "Ungültiger Parameter",
+               "Keine Berechtigung (Zugriffsrechte fehlen)",
+               "Kein passendes Gerät gefunden (es könnte entfernt worden sein)",
+               "Entität nicht gefunden",
+               "Die Ressource ist belegt",
+               "Die Wartezeit für die Operation ist abgelaufen",
+               "Mehr Daten empfangen als erwartet",
+               "Datenübergabe unterbrochen (broken pipe)",
+               "Unterbrechung während des Betriebssystemaufrufs",
+               "Nicht genügend Hauptspeicher verfügbar",
+               "Die Operation wird nicht unterstützt oder ist auf dieser Platform nicht implementiert",
+               "Allgemeiner Fehler",
+       }, { /* Hungarian (hu) */
+               "Sikeres",
+               "Be-/kimeneti hiba",
+               "Érvénytelen paraméter",
+               "Hozzáférés megtagadva",
+               "Az eszköz nem található (eltávolították?)",
+               "Nem található",
+               "Az erőforrás foglalt",
+               "Időtúllépés",
+               "Túlcsordulás",
+               "Törött adatcsatorna",
+               "Rendszerhívás megszakítva",
+               "Nincs elég memória",
+               "A művelet nem támogatott ezen a rendszeren",
+               "Általános hiba",
+       },
 };
 
+static const char * const (*usbi_error_strings)[LIBUSB_ERROR_COUNT] = &usbi_localized_errors[0];
+
 /** \ingroup libusb_misc
  * Set the language, and only the language, not the encoding! used for
  * translatable libusb messages.
@@ -134,7 +154,7 @@ static const char* usbi_localized_errors[ARRAYSIZE(usbi_locale_supported)][LIBUS
  * used, and only 2 letter ISO 639-1 codes are accepted for it, such as "de".
  * The optional region, country_region or codeset parts are ignored. This
  * means that functions which return translatable strings will NOT honor the
- * specified encoding. 
+ * specified encoding.
  * All strings returned are encoded as UTF-8 strings.
  *
  * If libusb_setlocale() is not called, all messages will be in English.
@@ -159,19 +179,20 @@ int API_EXPORTED libusb_setlocale(const char *locale)
 {
        size_t i;
 
-       if ( (locale == NULL) || (strlen(locale) < 2)
-         || ((strlen(locale) > 2) && (locale[2] != '-') && (locale[2] != '_') && (locale[2] != '.')) )
+       if (!locale || strlen(locale) < 2
+           || (locale[2] != '\0' && locale[2] != '-' && locale[2] != '_' && locale[2] != '.'))
                return LIBUSB_ERROR_INVALID_PARAM;
 
-       for (i=0; i<ARRAYSIZE(usbi_locale_supported); i++) {
-               if (strncasecmp(usbi_locale_supported[i], locale, 2) == 0)
+       for (i = 0; i < ARRAYSIZE(usbi_locale_supported); i++) {
+               if (usbi_locale_supported[i][0] == tolower((unsigned char)locale[0])
+                   && usbi_locale_supported[i][1] == tolower((unsigned char)locale[1]))
                        break;
        }
-       if (i >= ARRAYSIZE(usbi_locale_supported)) {
+
+       if (i == ARRAYSIZE(usbi_locale_supported))
                return LIBUSB_ERROR_NOT_FOUND;
-       }
 
-       usbi_locale = i;
+       usbi_error_strings = &usbi_localized_errors[i];
 
        return LIBUSB_SUCCESS;
 }
@@ -189,14 +210,14 @@ int API_EXPORTED libusb_setlocale(const char *locale)
  * \param errcode the error code whose description is desired
  * \returns a short description of the error code in UTF-8 encoding
  */
-DEFAULT_VISIBILITY const char* LIBUSB_CALL libusb_strerror(enum libusb_error errcode)
+DEFAULT_VISIBILITY const char * LIBUSB_CALL libusb_strerror(int errcode)
 {
        int errcode_index = -errcode;
 
-       if ((errcode_index < 0) || (errcode_index >= LIBUSB_ERROR_COUNT)) {
+       if (errcode_index < 0 || errcode_index >= LIBUSB_ERROR_COUNT) {
                /* "Other Error", which should always be our last message, is returned */
                errcode_index = LIBUSB_ERROR_COUNT - 1;
        }
 
-       return usbi_localized_errors[usbi_locale][errcode_index];
+       return (*usbi_error_strings)[errcode_index];
 }
index a609f65f44f41d4b289bf8adcaa3271fae801fae..adc95b4028f3d42218806eb048d36fa7b3c2d26e 100644 (file)
@@ -1,6 +1,9 @@
+/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */
 /*
  * Synchronous I/O functions for libusb
  * Copyright © 2007-2008 Daniel Drake <dsd@gentoo.org>
+ * Copyright © 2019 Nathan Hjelm <hjelmn@cs.unm.edu>
+ * Copyright © 2019 Google LLC. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#include <config.h>
+#include "libusbi.h"
 
-#include <errno.h>
-#include <stdint.h>
-#include <stdlib.h>
 #include <string.h>
 
-#include "libusbi.h"
-
 /**
  * @defgroup libusb_syncio Synchronous device I/O
  *
@@ -57,6 +55,11 @@ static void sync_transfer_wait_for_completion(struct libusb_transfer *transfer)
                        libusb_cancel_transfer(transfer);
                        continue;
                }
+               if (NULL == transfer->dev_handle) {
+                       /* transfer completion after libusb_close() */
+                       transfer->status = LIBUSB_TRANSFER_NO_DEVICE;
+                       *completed = 1;
+               }
        }
 }
 
@@ -78,7 +81,7 @@ static void sync_transfer_wait_for_completion(struct libusb_transfer *transfer)
  * (depending on direction bits within bmRequestType)
  * \param wLength the length field for the setup packet. The data buffer should
  * be at least this size.
- * \param timeout timeout (in millseconds) that this function should wait
+ * \param timeout timeout (in milliseconds) that this function should wait
  * before giving up due to no response being received. For an unlimited
  * timeout, use value 0.
  * \returns on success, the number of bytes actually transferred
@@ -88,7 +91,7 @@ static void sync_transfer_wait_for_completion(struct libusb_transfer *transfer)
  * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
  * \returns LIBUSB_ERROR_BUSY if called from event handling context
  * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than
- * the operating system and/or hardware can support
+ * the operating system and/or hardware can support (see \ref asynclimits)
  * \returns another LIBUSB_ERROR code on other failures
  */
 int API_EXPORTED libusb_control_transfer(libusb_device_handle *dev_handle,
@@ -107,7 +110,7 @@ int API_EXPORTED libusb_control_transfer(libusb_device_handle *dev_handle,
        if (!transfer)
                return LIBUSB_ERROR_NO_MEM;
 
-       buffer = (unsigned char*) malloc(LIBUSB_CONTROL_SETUP_SIZE + wLength);
+       buffer = malloc(LIBUSB_CONTROL_SETUP_SIZE + wLength);
        if (!buffer) {
                libusb_free_transfer(transfer);
                return LIBUSB_ERROR_NO_MEM;
@@ -240,7 +243,7 @@ static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle,
  * underlying O/S requirements, meaning that the timeout may expire after
  * the first few chunks have completed. libusb is careful not to lose any data
  * that may have been transferred; do not assume that timeout conditions
- * indicate a complete lack of I/O.
+ * indicate a complete lack of I/O. See \ref asynctimeout for more details.
  *
  * \param dev_handle a handle for the device to communicate with
  * \param endpoint the address of a valid endpoint to communicate with
@@ -252,7 +255,7 @@ static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle,
  * transferred. Since version 1.0.21 (\ref LIBUSB_API_VERSION >= 0x01000105),
  * it is legal to pass a NULL pointer if you do not wish to receive this
  * information.
- * \param timeout timeout (in millseconds) that this function should wait
+ * \param timeout timeout (in milliseconds) that this function should wait
  * before giving up due to no response being received. For an unlimited
  * timeout, use value 0.
  *
@@ -264,11 +267,13 @@ static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle,
  * \ref libusb_packetoverflow
  * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
  * \returns LIBUSB_ERROR_BUSY if called from event handling context
+ * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than
+ * the operating system and/or hardware can support (see \ref asynclimits)
  * \returns another LIBUSB_ERROR code on other failures
  */
-int API_EXPORTED libusb_bulk_transfer(struct libusb_device_handle *dev_handle,
-       unsigned char endpoint, unsigned char *data, int length, int *transferred,
-       unsigned int timeout)
+int API_EXPORTED libusb_bulk_transfer(libusb_device_handle *dev_handle,
+       unsigned char endpoint, unsigned char *data, int length,
+       int *transferred, unsigned int timeout)
 {
        return do_sync_bulk_transfer(dev_handle, endpoint, data, length,
                transferred, timeout, LIBUSB_TRANSFER_TYPE_BULK);
@@ -291,7 +296,7 @@ int API_EXPORTED libusb_bulk_transfer(struct libusb_device_handle *dev_handle,
  * underlying O/S requirements, meaning that the timeout may expire after
  * the first few chunks have completed. libusb is careful not to lose any data
  * that may have been transferred; do not assume that timeout conditions
- * indicate a complete lack of I/O.
+ * indicate a complete lack of I/O. See \ref asynctimeout for more details.
  *
  * The default endpoint bInterval value is used as the polling interval.
  *
@@ -305,7 +310,7 @@ int API_EXPORTED libusb_bulk_transfer(struct libusb_device_handle *dev_handle,
  * transferred. Since version 1.0.21 (\ref LIBUSB_API_VERSION >= 0x01000105),
  * it is legal to pass a NULL pointer if you do not wish to receive this
  * information.
- * \param timeout timeout (in millseconds) that this function should wait
+ * \param timeout timeout (in milliseconds) that this function should wait
  * before giving up due to no response being received. For an unlimited
  * timeout, use value 0.
  *
@@ -316,11 +321,13 @@ int API_EXPORTED libusb_bulk_transfer(struct libusb_device_handle *dev_handle,
  * \ref libusb_packetoverflow
  * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
  * \returns LIBUSB_ERROR_BUSY if called from event handling context
+ * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than
+ * the operating system and/or hardware can support (see \ref asynclimits)
  * \returns another LIBUSB_ERROR code on other error
  */
-int API_EXPORTED libusb_interrupt_transfer(
-       struct libusb_device_handle *dev_handle, unsigned char endpoint,
-       unsigned char *data, int length, int *transferred, unsigned int timeout)
+int API_EXPORTED libusb_interrupt_transfer(libusb_device_handle *dev_handle,
+       unsigned char endpoint, unsigned char *data, int length,
+       int *transferred, unsigned int timeout)
 {
        return do_sync_bulk_transfer(dev_handle, endpoint, data, length,
                transferred, timeout, LIBUSB_TRANSFER_TYPE_INTERRUPT);
index c6dfe370934450cb2439a87af191206d32df4e93..d8ebde4e394d6e1b072f673e281361402bb937b9 100644 (file)
@@ -7,7 +7,7 @@
 #define LIBUSB_MINOR 0
 #endif
 #ifndef LIBUSB_MICRO
-#define LIBUSB_MICRO 22
+#define LIBUSB_MICRO 24
 #endif
 #ifndef LIBUSB_NANO
 #define LIBUSB_NANO 0
index 90a782a6bf943ad3f2c286a5573d63d14b8588e8..0f100a82478c5c09f44f31712680d4a7d6157a8d 100644 (file)
@@ -1 +1 @@
-#define LIBUSB_NANO 11312
+#define LIBUSB_NANO 11584